From afd3ca8273c717d1a74169bae19f2d26cc13694b Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 29 Jan 2022 21:40:02 +0200 Subject: [PATCH] gx: implement C1.over(C2), add gx.cyan and gx.magenta, fix + and - ops, add more tests --- vlib/gx/color.v | 114 +++++++++++++++++++++++++++++++++---------- vlib/gx/color_test.v | 54 +++++++++++++++++--- 2 files changed, 134 insertions(+), 34 deletions(-) diff --git a/vlib/gx/color.v b/vlib/gx/color.v index 02adf891b3..14ea53f78a 100644 --- a/vlib/gx/color.v +++ b/vlib/gx/color.v @@ -1,9 +1,19 @@ module gx pub const ( - blue = Color{ + black = Color{ r: 0 g: 0 + b: 0 + } + gray = Color{ + r: 128 + g: 128 + b: 128 + } + white = Color{ + r: 255 + g: 255 b: 255 } red = Color{ @@ -16,11 +26,26 @@ pub const ( g: 255 b: 0 } + blue = Color{ + r: 0 + g: 0 + b: 255 + } yellow = Color{ r: 255 g: 255 b: 0 } + magenta = Color{ + r: 255 + g: 0 + b: 255 + } + cyan = Color{ + r: 0 + g: 255 + b: 255 + } orange = Color{ r: 255 g: 165 @@ -31,16 +56,6 @@ pub const ( g: 0 b: 128 } - black = Color{ - r: 0 - g: 0 - b: 0 - } - gray = Color{ - r: 128 - g: 128 - b: 128 - } indigo = Color{ r: 75 g: 0 @@ -56,11 +71,6 @@ pub const ( g: 130 b: 238 } - white = Color{ - r: 255 - g: 255 - b: 255 - } dark_blue = Color{ r: 0 g: 0 @@ -139,21 +149,55 @@ pub fn rgba(r byte, g byte, b byte, a byte) Color { } } -pub fn (c Color) + (c2 Color) Color { +// + adds `b` to `a`, with a maximum value of 255 for each channel +pub fn (a Color) + (b Color) Color { + mut na := int(a.a) + b.a + mut nr := int(a.r) + b.r + mut ng := int(a.g) + b.g + mut nb := int(a.b) + b.b + if na > 255 { + na = 255 + } + if nr > 255 { + nr = 255 + } + if ng > 255 { + ng = 255 + } + if nb > 255 { + nb = 255 + } return Color{ - r: c.r + c2.r - g: c.g + c2.g - b: c.b + c2.b - a: c.a + c2.a + r: byte(nr) + g: byte(ng) + b: byte(nb) + a: byte(na) } } -pub fn (c Color) - (c2 Color) Color { +// - subtracts `b` from `a`, with a minimum value of 0 for each channel +pub fn (a Color) - (b Color) Color { + mut na := if a.a > b.a { a.a } else { b.a } + mut nr := int(a.r) - b.r + mut ng := int(a.g) - b.g + mut nb := int(a.b) - b.b + if na < 0 { + na = 0 + } + if nr < 0 { + nr = 0 + } + if ng < 0 { + ng = 0 + } + if nb < 0 { + nb = 0 + } return Color{ - r: c.r - c2.r - g: c.g - c2.g - b: c.b - c2.b - a: c.a - c2.a + r: byte(nr) + g: byte(ng) + b: byte(nb) + a: byte(na) } } @@ -175,6 +219,24 @@ pub fn (c Color) / (c2 Color) Color { } } +// over - implements an `a` over `b` operation. +// see https://keithp.com/~keithp/porterduff/p253-porter.pdf +pub fn (a Color) over(b Color) Color { + aa := f32(a.a) / 255 + ab := f32(b.a) / 255 + ar := aa + ab * (1 - aa) + // + rr := (f32(a.r) * aa + f32(b.r) * ab * (1 - aa)) / ar + gr := (f32(a.g) * aa + f32(b.g) * ab * (1 - aa)) / ar + br := (f32(a.b) * aa + f32(b.b) * ab * (1 - aa)) / ar + return Color{ + r: byte(rr) + g: byte(gr) + b: byte(br) + a: byte(ar * 255) + } +} + pub fn (c Color) eq(c2 Color) bool { return c.r == c2.r && c.g == c2.g && c.b == c2.b && c.a == c2.a } diff --git a/vlib/gx/color_test.v b/vlib/gx/color_test.v index 533a41ddf7..ace27e95f0 100644 --- a/vlib/gx/color_test.v +++ b/vlib/gx/color_test.v @@ -4,38 +4,47 @@ fn test_hex() { // valid colors a := gx.hex(0x6c5ce7ff) b := gx.rgba(108, 92, 231, 255) - assert a.eq(b) + assert a == b // doesn't give right value with short hex value short := gx.hex(0xfff) - assert !short.eq(gx.white) + assert short != gx.white + assert short == gx.Color{0, 0, 15, 255} } fn test_add() { a := gx.rgba(100, 100, 100, 100) b := gx.rgba(100, 100, 100, 100) r := gx.rgba(200, 200, 200, 200) - assert (a + b).eq(r) + assert (a + b) == r + // + assert gx.red + gx.green == gx.yellow + assert gx.red + gx.blue == gx.magenta + assert gx.green + gx.blue == gx.cyan } fn test_sub() { - a := gx.rgba(100, 100, 100, 100) + a := gx.rgba(100, 100, 100, 50) b := gx.rgba(100, 100, 100, 100) - r := gx.rgba(0, 0, 0, 0) - assert (a - b).eq(r) + r := gx.rgba(0, 0, 0, 100) + assert (a - b) == r + // + assert gx.white - gx.green == gx.magenta + assert gx.white - gx.blue == gx.yellow + assert gx.white - gx.red == gx.cyan } fn test_mult() { a := gx.rgba(10, 10, 10, 10) b := gx.rgba(10, 10, 10, 10) r := gx.rgba(100, 100, 100, 100) - assert (a * b).eq(r) + assert (a * b) == r } fn test_div() { a := gx.rgba(100, 100, 100, 100) b := gx.rgba(10, 10, 10, 10) r := gx.rgba(10, 10, 10, 10) - assert (a / b).eq(r) + assert (a / b) == r } fn test_rgba8() { @@ -61,3 +70,32 @@ fn test_abgr8() { assert gx.green.abgr8() == -16711936 assert gx.blue.abgr8() == -65536 } + +fn test_over() { + // shorten names: + r := gx.red + g := gx.green + b := gx.blue + y := gx.yellow + semi_r := gx.Color{255, 0, 0, 128} + semi_g := gx.Color{0, 255, 0, 128} + semi_b := gx.Color{0, 0, 255, 128} + // fully opaque colors, should be preserved when layed *over* any others: + assert b.over(g) == b + assert r.over(g) == r + assert y.over(r) == y + assert g.over(r) == g + + // half transparent pure colors, *over* pure colors, should preserve them correspondingly: + assert semi_r.over(r) == gx.Color{255, 0, 0, 255} // preserve pure red + assert semi_r.over(g) == gx.Color{128, 126, 0, 255} + assert semi_r.over(b) == gx.Color{128, 0, 126, 255} + + assert semi_g.over(r) == gx.Color{126, 128, 0, 255} + assert semi_g.over(g) == gx.Color{0, 255, 0, 255} // preserve pure green + assert semi_g.over(b) == gx.Color{0, 128, 126, 255} + + assert semi_b.over(r) == gx.Color{126, 0, 128, 255} + assert semi_b.over(g) == gx.Color{0, 126, 128, 255} + assert semi_b.over(b) == gx.Color{0, 0, 255, 255} // preserve pure blue +}