theme: store colors in linear space
This commit is contained in:
parent
b7f93b37a6
commit
135f37dbcd
27 changed files with 221 additions and 135 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{GlobalArgs, color::parse_color, duration::parse_duration},
|
||||
theme::TransferFunction,
|
||||
tools::tool_client::{ToolClient, with_tool_client},
|
||||
wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled},
|
||||
},
|
||||
|
|
@ -85,12 +86,13 @@ impl DamageTracking {
|
|||
}
|
||||
DamageTrackingCmd::SetColor(c) => {
|
||||
let color = parse_color(&c.color);
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
tc.send(SetVisualizerColor {
|
||||
self_id: dt,
|
||||
r: color.r,
|
||||
g: color.g,
|
||||
b: color.b,
|
||||
a: color.a,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a,
|
||||
});
|
||||
}
|
||||
DamageTrackingCmd::SetDecay(c) => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use {
|
|||
output_schedule::map_cursor_hz,
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
||||
theme::{Color, ThemeSized},
|
||||
theme::{Color, ThemeSized, TransferFunction},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
||||
|
|
@ -1599,8 +1599,8 @@ impl ConfigProxyHandler {
|
|||
|
||||
fn handle_get_color(&self, colorable: Colorable) -> Result<(), CphError> {
|
||||
let color = self.get_color(colorable)?.get();
|
||||
let color =
|
||||
jay_config::theme::Color::new_f32_premultiplied(color.r, color.g, color.b, color.a);
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
let color = jay_config::theme::Color::new_f32_premultiplied(r, g, b, a);
|
||||
self.respond(Response::GetColor { color });
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ impl DamageVisualizer {
|
|||
entry_added: Default::default(),
|
||||
enabled: Default::default(),
|
||||
decay: Cell::new(Duration::from_secs(2)),
|
||||
color: Cell::new(Color::from_rgba_straight(255, 0, 0, 128)),
|
||||
color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -352,25 +352,16 @@ impl dyn GfxFramebuffer {
|
|||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clear_with(acquire_sync, release_sync, 0.0, 0.0, 0.0, 0.0)
|
||||
self.clear_with(acquire_sync, release_sync, &Color::TRANSPARENT)
|
||||
}
|
||||
|
||||
pub fn clear_with(
|
||||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
color: &Color,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
&[],
|
||||
Some(&Color { r, g, b, a }),
|
||||
None,
|
||||
)
|
||||
self.render(acquire_sync, release_sync, &[], Some(color), None)
|
||||
}
|
||||
|
||||
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ use {
|
|||
GL_TRIANGLE_STRIP, GL_TRIANGLES,
|
||||
},
|
||||
},
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage},
|
||||
video::{
|
||||
dmabuf::DMA_BUF_SYNC_READ,
|
||||
|
|
@ -305,10 +305,11 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
|||
}
|
||||
|
||||
fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) {
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
unsafe {
|
||||
(gles.glUseProgram)(ctx.fill_prog.prog);
|
||||
(gles.glUniform4f)(ctx.fill_prog_color, color.r, color.g, color.b, color.a);
|
||||
(gles.glUniform4f)(ctx.fill_prog_color, r, g, b, a);
|
||||
(gles.glVertexAttribPointer)(
|
||||
ctx.fill_prog_pos as _,
|
||||
2,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use {
|
|||
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||
},
|
||||
rect::Region,
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
|
|
@ -78,7 +78,8 @@ impl Framebuffer {
|
|||
(gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo);
|
||||
(gles.glViewport)(0, 0, self.gl.width, self.gl.height);
|
||||
if let Some(c) = clear {
|
||||
(gles.glClearColor)(c.r, c.g, c.b, c.a);
|
||||
let [r, g, b, a] = c.to_array(TransferFunction::Srgb);
|
||||
(gles.glClearColor)(r, g, b, a);
|
||||
(gles.glClear)(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
(gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use {
|
|||
},
|
||||
io_uring::IoUring,
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
utils::{
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, once::Once,
|
||||
stack::Stack,
|
||||
|
|
@ -589,8 +589,8 @@ impl VulkanRenderer {
|
|||
let clear_value = ClearValue {
|
||||
color: ClearColorValue {
|
||||
float32: match pass {
|
||||
RenderPass::BlendBuffer => clear.to_array_linear(None),
|
||||
RenderPass::FrameBuffer => clear.to_array_srgb(None),
|
||||
RenderPass::BlendBuffer => clear.to_array(TransferFunction::Linear),
|
||||
RenderPass::FrameBuffer => clear.to_array(TransferFunction::Srgb),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -704,8 +704,12 @@ impl VulkanRenderer {
|
|||
let push = FillPushConstants {
|
||||
pos: r.rect.to_points(),
|
||||
color: match pass {
|
||||
RenderPass::BlendBuffer => r.color.to_array_linear(r.alpha),
|
||||
RenderPass::FrameBuffer => r.color.to_array_srgb(r.alpha),
|
||||
RenderPass::BlendBuffer => {
|
||||
r.color.to_array2(TransferFunction::Linear, r.alpha)
|
||||
}
|
||||
RenderPass::FrameBuffer => {
|
||||
r.color.to_array2(TransferFunction::Srgb, r.alpha)
|
||||
}
|
||||
},
|
||||
};
|
||||
let source_type = match push.color[3] < 1.0 {
|
||||
|
|
@ -1309,7 +1313,7 @@ impl VulkanRenderer {
|
|||
for opt in opts.iter().rev() {
|
||||
let (opaque, fb_rect) = match opt {
|
||||
GfxApiOpt::Sync => continue,
|
||||
GfxApiOpt::FillRect(f) => (f.effective_color().a >= 1.0, f.rect),
|
||||
GfxApiOpt::FillRect(f) => (f.effective_color().is_opaque(), f.rect),
|
||||
GfxApiOpt::CopyTexture(c) => {
|
||||
let opaque = 'opaque: {
|
||||
if let Some(a) = c.alpha {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
globals::{Global, GlobalName},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
wire::{
|
||||
JayCompositorId,
|
||||
jay_damage_tracking::{
|
||||
|
|
@ -96,12 +96,8 @@ impl JayDamageTrackingRequestHandler for JayDamageTracking {
|
|||
req: SetVisualizerColor,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.client.state.damage_visualizer.set_color(Color {
|
||||
r: req.r,
|
||||
g: req.g,
|
||||
b: req.b,
|
||||
a: req.a,
|
||||
});
|
||||
let color = Color::new(TransferFunction::Srgb, req.r, req.g, req.b) * req.a;
|
||||
self.client.state.damage_visualizer.set_color(color);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use {
|
|||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
utils::errorfmt::ErrorFmt,
|
||||
video::dmabuf::DmaBuf,
|
||||
wire::{WlBufferId, wl_buffer::*},
|
||||
|
|
@ -42,7 +41,7 @@ pub struct WlBuffer {
|
|||
render_ctx_version: Cell<u32>,
|
||||
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||
shm: bool,
|
||||
pub color: Option<Color>,
|
||||
pub color: Option<[u32; 4]>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
pub tracker: Tracker<Self>,
|
||||
|
|
@ -149,7 +148,7 @@ impl WlBuffer {
|
|||
width: 1,
|
||||
height: 1,
|
||||
tracker: Default::default(),
|
||||
color: Some(Color::from_u32_rgba_premultiplied(r, g, b, a)),
|
||||
color: Some([r, g, b, a]),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
a = 255;
|
||||
}
|
||||
staging[(y * width + x) as usize] =
|
||||
Color::from_rgba_premultiplied(r, g, b, a);
|
||||
Color::from_srgba_premultiplied(r, g, b, a);
|
||||
}
|
||||
data = data.add(stride as usize);
|
||||
}
|
||||
|
|
@ -446,7 +446,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let [r, g, b, a] =
|
||||
staging[(y * width + x) as usize].to_rgba_premultiplied();
|
||||
staging[(y * width + x) as usize].to_srgba_premultiplied();
|
||||
*data.add((x * 4) as usize).cast::<[u8; 4]>() = [b, g, r, a];
|
||||
}
|
||||
data = data.add(stride as usize);
|
||||
|
|
@ -499,7 +499,7 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
if !t_format.has_alpha {
|
||||
a = 255;
|
||||
}
|
||||
let mut color = Color::from_rgba_premultiplied(r, g, b, a);
|
||||
let mut color = Color::from_srgba_premultiplied(r, g, b, a);
|
||||
if let Some(alpha) = c.alpha {
|
||||
color = color * alpha;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub struct TestShmBuffer {
|
|||
|
||||
impl TestShmBuffer {
|
||||
pub fn fill(&self, color: Color) {
|
||||
let [cr, cg, cb, ca] = color.to_rgba_premultiplied();
|
||||
let [cr, cg, cb, ca] = color.to_srgba_premultiplied();
|
||||
for [b, g, r, a] in self.deref().array_chunks_ext::<4>() {
|
||||
r.set(cr);
|
||||
g.set(cg);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
test_error::TestResult, test_ifs::test_buffer::TestBuffer, test_object::TestObject,
|
||||
test_transport::TestTransport,
|
||||
},
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
wire::{WpSinglePixelBufferManagerV1Id, wp_single_pixel_buffer_manager_v1::*},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
|
|
@ -31,13 +31,14 @@ impl TestSinglePixelBufferManager {
|
|||
destroyed: Cell::new(false),
|
||||
});
|
||||
let map = |c: f32| (c as f64 * u32::MAX as f64) as u32;
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
self.tran.send(CreateU32RgbaBuffer {
|
||||
self_id: self.id,
|
||||
id: obj.id,
|
||||
r: map(color.r),
|
||||
g: map(color.g),
|
||||
b: map(color.b),
|
||||
a: map(color.a),
|
||||
r: map(r),
|
||||
g: map(g),
|
||||
b: map(b),
|
||||
a: map(a),
|
||||
})?;
|
||||
self.tran.add_obj(obj.clone())?;
|
||||
Ok(obj)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,6 @@ impl TestSurfaceExt {
|
|||
}
|
||||
|
||||
pub fn set_color(&self, r: u8, g: u8, b: u8, a: u8) {
|
||||
self.color.set(Color::from_rgba_straight(r, g, b, a));
|
||||
self.color.set(Color::from_srgba_straight(r, g, b, a));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
|||
|
||||
let buffer = client
|
||||
.spbm
|
||||
.create_buffer(Color::from_rgba_straight(255, 255, 255, 255))?;
|
||||
.create_buffer(Color::from_srgba_straight(255, 255, 255, 255))?;
|
||||
|
||||
child.attach(buffer.id)?;
|
||||
child_viewport.set_source(0, 0, 1, 1)?;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
|||
let win1 = client.create_window().await?;
|
||||
win1.map2().await?;
|
||||
|
||||
let buffer = client.spbm.create_buffer(Color::from_rgb(255, 0, 0))?;
|
||||
let buffer = client.spbm.create_buffer(Color::from_srgb(255, 0, 0))?;
|
||||
let surface = client.comp.create_surface().await?;
|
||||
let vp = client.viewporter.get_viewport(&surface)?;
|
||||
vp.set_destination(100, 100)?;
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
}};
|
||||
}
|
||||
|
||||
let buf1 = client.spbm.create_buffer(Color::from_rgb(0, 255, 0))?;
|
||||
let buf1 = client.spbm.create_buffer(Color::from_srgb(0, 255, 0))?;
|
||||
let (ss1, alpha1) = create_surface!(&buf1, 0, 0);
|
||||
|
||||
let buf2 = client.shm.create_buffer(1, 1)?;
|
||||
buf2.fill(Color::from_rgb(0, 255, 0));
|
||||
buf2.fill(Color::from_srgb(0, 255, 0));
|
||||
let (ss2, alpha2) = create_surface!(&buf2.buffer, 100, 0);
|
||||
|
||||
client.compare_screenshot("1", false).await?;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -67,18 +67,18 @@ fn create_accept_gui(surface: &Rc<SelectionGuiSurface>) -> Rc<dyn GuiElement> {
|
|||
let accept_button = static_button(surface, ButtonRole::Accept, "Allow");
|
||||
let reject_button = static_button(surface, ButtonRole::Reject, "Reject");
|
||||
for button in [&accept_button, &reject_button] {
|
||||
button.border_color.set(Color::from_gray(100));
|
||||
button.border_color.set(Color::from_gray_srgb(100));
|
||||
button.border.set(2.0);
|
||||
button.padding.set(5.0);
|
||||
}
|
||||
accept_button.bg_color.set(Color::from_rgb(170, 200, 170));
|
||||
accept_button.bg_color.set(Color::from_srgb(170, 200, 170));
|
||||
accept_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(170, 255, 170));
|
||||
reject_button.bg_color.set(Color::from_rgb(200, 170, 170));
|
||||
.set(Color::from_srgb(170, 255, 170));
|
||||
reject_button.bg_color.set(Color::from_srgb(200, 170, 170));
|
||||
reject_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(255, 170, 170));
|
||||
.set(Color::from_srgb(255, 170, 170));
|
||||
let flow = Rc::new(Flow::default());
|
||||
flow.orientation.set(Orientation::Vertical);
|
||||
flow.cross_align.set(Align::Center);
|
||||
|
|
|
|||
|
|
@ -87,22 +87,22 @@ fn create_accept_gui(surface: &Rc<SelectionGuiSurface>, for_restore: bool) -> Rc
|
|||
&window_button,
|
||||
&reject_button,
|
||||
] {
|
||||
button.border_color.set(Color::from_gray(100));
|
||||
button.border_color.set(Color::from_gray_srgb(100));
|
||||
button.border.set(2.0);
|
||||
button.padding.set(5.0);
|
||||
}
|
||||
restore_button.bg_color.set(Color::from_rgb(170, 170, 200));
|
||||
restore_button.bg_color.set(Color::from_srgb(170, 170, 200));
|
||||
restore_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(170, 170, 255));
|
||||
.set(Color::from_srgb(170, 170, 255));
|
||||
for button in [&accept_button, &workspace_button, &window_button] {
|
||||
button.bg_color.set(Color::from_rgb(170, 200, 170));
|
||||
button.bg_hover_color.set(Color::from_rgb(170, 255, 170));
|
||||
button.bg_color.set(Color::from_srgb(170, 200, 170));
|
||||
button.bg_hover_color.set(Color::from_srgb(170, 255, 170));
|
||||
}
|
||||
reject_button.bg_color.set(Color::from_rgb(200, 170, 170));
|
||||
reject_button.bg_color.set(Color::from_srgb(200, 170, 170));
|
||||
reject_button
|
||||
.bg_hover_color
|
||||
.set(Color::from_rgb(255, 170, 170));
|
||||
.set(Color::from_srgb(255, 170, 170));
|
||||
let flow = Rc::new(Flow::default());
|
||||
flow.orientation.set(Orientation::Vertical);
|
||||
flow.cross_align.set(Align::Center);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use {
|
|||
consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE},
|
||||
},
|
||||
rect::Rect,
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
},
|
||||
std::{ops::Neg, rc::Rc, sync::Arc},
|
||||
};
|
||||
|
|
@ -78,9 +78,9 @@ pub fn render(
|
|||
let data = create_data(font, width, height, scale)?;
|
||||
data.layout.set_text(text);
|
||||
let font_height = data.layout.pixel_size().1;
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
||||
data.cctx
|
||||
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
|
||||
data.cctx.set_source_rgba(r as _, g as _, b as _, a as _);
|
||||
let y = y.unwrap_or((height - font_height) / 2);
|
||||
data.cctx.move_to(x as f64, y as f64);
|
||||
data.layout.show_layout();
|
||||
|
|
|
|||
|
|
@ -139,9 +139,9 @@ impl Default for Button {
|
|||
hover: Default::default(),
|
||||
padding: Default::default(),
|
||||
border: Default::default(),
|
||||
border_color: Cell::new(Color::from_gray(0)),
|
||||
bg_color: Cell::new(Color::from_gray(255)),
|
||||
bg_hover_color: Cell::new(Color::from_gray(255)),
|
||||
border_color: Cell::new(Color::from_gray_srgb(0)),
|
||||
bg_color: Cell::new(Color::from_gray_srgb(255)),
|
||||
bg_hover_color: Cell::new(Color::from_gray_srgb(255)),
|
||||
text: Default::default(),
|
||||
font: Arc::new(DEFAULT_FONT.to_string()),
|
||||
tex: Default::default(),
|
||||
|
|
@ -172,7 +172,7 @@ impl GuiElement for Button {
|
|||
None,
|
||||
&self.font,
|
||||
&text,
|
||||
Color::from_gray(0),
|
||||
Color::from_gray_srgb(0),
|
||||
Some(scale as _),
|
||||
true,
|
||||
);
|
||||
|
|
@ -296,7 +296,7 @@ impl GuiElement for Label {
|
|||
None,
|
||||
&self.font,
|
||||
&text,
|
||||
Color::from_gray(255),
|
||||
Color::from_gray_srgb(255),
|
||||
Some(scale as _),
|
||||
false,
|
||||
);
|
||||
|
|
@ -635,7 +635,7 @@ impl WindowData {
|
|||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
self.scale.get(),
|
||||
Some(&Color::from_gray(0)),
|
||||
Some(&Color::from_gray_srgb(0)),
|
||||
None,
|
||||
&mut |r| {
|
||||
if let Some(content) = self.content.get() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use {
|
|||
renderer::renderer_base::RendererBase,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
tree::{
|
||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||
ToplevelNodeBase, WorkspaceNode,
|
||||
|
|
@ -202,7 +202,7 @@ impl Renderer<'_> {
|
|||
let pos = placeholder.tl_data().pos.get();
|
||||
self.base.fill_boxes(
|
||||
std::slice::from_ref(&pos.at_point(x, y)),
|
||||
&Color::from_rgba_straight(20, 20, 20, 255),
|
||||
&Color::from_srgba_straight(20, 20, 20, 255),
|
||||
);
|
||||
if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale) {
|
||||
if let Some(texture) = tex.texture() {
|
||||
|
|
@ -448,8 +448,15 @@ impl Renderer<'_> {
|
|||
Some(bounds) => rect.intersect(*bounds),
|
||||
};
|
||||
if !rect.is_empty() {
|
||||
let color = Color::from_u32_premultiplied(
|
||||
TransferFunction::Srgb,
|
||||
color[0],
|
||||
color[1],
|
||||
color[2],
|
||||
color[3],
|
||||
);
|
||||
self.base.ops.push(GfxApiOpt::Sync);
|
||||
self.base.fill_scaled_boxes(&[rect], color, alpha);
|
||||
self.base.fill_scaled_boxes(&[rect], &color, alpha);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use {
|
|||
},
|
||||
},
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
theme::{Color, TransferFunction},
|
||||
utils::{
|
||||
clonecell::CloneCell, double_buffered::DoubleBuffered, on_drop_event::OnDropEvent,
|
||||
},
|
||||
|
|
@ -187,9 +187,9 @@ fn render(
|
|||
data.layout.set_text(text);
|
||||
}
|
||||
let font_height = data.layout.pixel_size().1;
|
||||
let [r, g, b, a] = color.to_array(TransferFunction::Srgb);
|
||||
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
||||
data.cctx
|
||||
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
|
||||
data.cctx.set_source_rgba(r as _, g as _, b as _, a as _);
|
||||
let y = y.unwrap_or((height - font_height) / 2);
|
||||
data.cctx.move_to(x as f64, y as f64);
|
||||
data.layout.show_layout();
|
||||
|
|
|
|||
192
src/theme.rs
192
src/theme.rs
|
|
@ -3,12 +3,18 @@ use {
|
|||
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TransferFunction {
|
||||
Srgb,
|
||||
Linear,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
r: f32,
|
||||
g: f32,
|
||||
b: f32,
|
||||
a: f32,
|
||||
}
|
||||
|
||||
impl Eq for Color {}
|
||||
|
|
@ -65,77 +71,155 @@ impl Color {
|
|||
a: 1.0,
|
||||
};
|
||||
|
||||
pub fn from_gray(g: u8) -> Self {
|
||||
Self::from_rgb(g, g, g)
|
||||
pub fn new(transfer_function: TransferFunction, mut r: f32, mut g: f32, mut b: f32) -> Self {
|
||||
fn srgb(c: f32) -> f32 {
|
||||
if c <= 0.04045 {
|
||||
c / 12.92
|
||||
} else {
|
||||
((c + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn linear(c: f32) -> f32 {
|
||||
c
|
||||
}
|
||||
macro_rules! convert {
|
||||
($tf:ident) => {{
|
||||
r = $tf(r);
|
||||
g = $tf(g);
|
||||
b = $tf(b);
|
||||
}};
|
||||
}
|
||||
match transfer_function {
|
||||
TransferFunction::Srgb => convert!(srgb),
|
||||
TransferFunction::Linear => convert!(linear),
|
||||
}
|
||||
Self { r, g, b, a: 1.0 }
|
||||
}
|
||||
|
||||
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
Self {
|
||||
r: to_f32(r),
|
||||
g: to_f32(g),
|
||||
b: to_f32(b),
|
||||
a: 1.0,
|
||||
pub fn new_premultiplied(
|
||||
transfer_function: TransferFunction,
|
||||
mut r: f32,
|
||||
mut g: f32,
|
||||
mut b: f32,
|
||||
a: f32,
|
||||
) -> Self {
|
||||
if transfer_function == TransferFunction::Linear {
|
||||
return Self { r, g, b, a };
|
||||
}
|
||||
if a < 1.0 && a > 0.0 {
|
||||
for c in [&mut r, &mut g, &mut b] {
|
||||
*c /= a;
|
||||
}
|
||||
}
|
||||
let mut c = Self::new(transfer_function, r, g, b);
|
||||
if a < 1.0 {
|
||||
c = c * a;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
self.a >= 1.0
|
||||
}
|
||||
|
||||
pub fn from_gray_srgb(g: u8) -> Self {
|
||||
Self::from_srgb(g, g, g)
|
||||
}
|
||||
|
||||
pub fn from_srgb(r: u8, g: u8, b: u8) -> Self {
|
||||
Self::new(TransferFunction::Srgb, to_f32(r), to_f32(g), to_f32(b))
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||
pub fn from_rgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self {
|
||||
r: to_f32(r),
|
||||
g: to_f32(g),
|
||||
b: to_f32(b),
|
||||
a: to_f32(a),
|
||||
}
|
||||
pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self::new_premultiplied(
|
||||
TransferFunction::Srgb,
|
||||
to_f32(r),
|
||||
to_f32(g),
|
||||
to_f32(b),
|
||||
to_f32(a),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_u32_rgba_premultiplied(r: u32, g: u32, b: u32, a: u32) -> Self {
|
||||
pub fn from_u32_premultiplied(
|
||||
transfer_function: TransferFunction,
|
||||
r: u32,
|
||||
g: u32,
|
||||
b: u32,
|
||||
a: u32,
|
||||
) -> Self {
|
||||
fn to_f32(c: u32) -> f32 {
|
||||
((c as f64) / (u32::MAX as f64)) as f32
|
||||
}
|
||||
Self {
|
||||
r: to_f32(r),
|
||||
g: to_f32(g),
|
||||
b: to_f32(b),
|
||||
a: to_f32(a),
|
||||
}
|
||||
Self::new_premultiplied(
|
||||
transfer_function,
|
||||
to_f32(r),
|
||||
to_f32(g),
|
||||
to_f32(b),
|
||||
to_f32(a),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_rgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
let alpha = to_f32(a);
|
||||
Self {
|
||||
r: to_f32(r) * alpha,
|
||||
g: to_f32(g) * alpha,
|
||||
b: to_f32(b) * alpha,
|
||||
a: alpha,
|
||||
pub fn from_srgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
let mut c = Self::new(TransferFunction::Srgb, to_f32(r), to_f32(g), to_f32(b));
|
||||
if a < 255 {
|
||||
c = c * to_f32(a);
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||
pub fn to_rgba_premultiplied(self) -> [u8; 4] {
|
||||
[to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)]
|
||||
pub fn to_srgba_premultiplied(self) -> [u8; 4] {
|
||||
let [r, g, b, a] = self.to_array(TransferFunction::Srgb);
|
||||
[to_u8(r), to_u8(g), to_u8(b), to_u8(a)]
|
||||
}
|
||||
|
||||
pub fn to_array_srgb(self, alpha: Option<f32>) -> [f32; 4] {
|
||||
let a = alpha.unwrap_or(1.0);
|
||||
[self.r * a, self.g * a, self.b * a, self.a * a]
|
||||
pub fn to_array(self, transfer_function: TransferFunction) -> [f32; 4] {
|
||||
self.to_array2(transfer_function, None)
|
||||
}
|
||||
|
||||
pub fn to_array_linear(self, alpha: Option<f32>) -> [f32; 4] {
|
||||
fn to_linear(srgb: f32) -> f32 {
|
||||
if srgb <= 0.04045 {
|
||||
srgb / 12.92
|
||||
pub fn to_array2(self, transfer_function: TransferFunction, alpha: Option<f32>) -> [f32; 4] {
|
||||
let mut res = [self.r, self.g, self.b, self.a];
|
||||
fn srgb(c: f32) -> f32 {
|
||||
if c <= 0.0031308 {
|
||||
c * 12.92
|
||||
} else {
|
||||
((srgb + 0.055) / 1.055).powf(2.4)
|
||||
1.055 * c.powf(1.0 / 2.4) - 0.055
|
||||
}
|
||||
}
|
||||
let a1 = if self.a == 0.0 { 1.0 } else { self.a };
|
||||
let a2 = self.a * alpha.unwrap_or(1.0);
|
||||
[
|
||||
to_linear(self.r / a1) * a2,
|
||||
to_linear(self.g / a1) * a2,
|
||||
to_linear(self.b / a1) * a2,
|
||||
a2,
|
||||
]
|
||||
fn linear(c: f32) -> f32 {
|
||||
c
|
||||
}
|
||||
macro_rules! convert {
|
||||
($tf:ident) => {{
|
||||
for c in &mut res[..3] {
|
||||
*c = $tf(*c);
|
||||
}
|
||||
}};
|
||||
}
|
||||
if transfer_function != TransferFunction::Linear {
|
||||
if self.a < 1.0 && self.a > 0.0 {
|
||||
for c in &mut res[..3] {
|
||||
*c /= self.a;
|
||||
}
|
||||
}
|
||||
match transfer_function {
|
||||
TransferFunction::Srgb => convert!(srgb),
|
||||
TransferFunction::Linear => convert!(linear),
|
||||
}
|
||||
if self.a < 1.0 {
|
||||
for c in &mut res[..3] {
|
||||
*c *= self.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(a) = alpha {
|
||||
for c in &mut res {
|
||||
*c *= a;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "it"), expect(dead_code))]
|
||||
|
|
@ -152,7 +236,7 @@ impl Color {
|
|||
impl From<jay_config::theme::Color> for Color {
|
||||
fn from(f: jay_config::theme::Color) -> Self {
|
||||
let [r, g, b, a] = f.to_f32_premultiplied();
|
||||
Self { r, g, b, a }
|
||||
Self::new_premultiplied(TransferFunction::Srgb, r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,10 +268,10 @@ macro_rules! colors {
|
|||
}
|
||||
};
|
||||
(@colors ($r:expr, $g:expr, $b:expr)) => {
|
||||
Color::from_rgb($r, $g, $b)
|
||||
Color::from_srgb($r, $g, $b)
|
||||
};
|
||||
(@colors ($r:expr, $g:expr, $b:expr, $a:expr)) => {
|
||||
Color::from_rgba_straight($r, $g, $b, $a)
|
||||
Color::from_srgba_straight($r, $g, $b, $a)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue