diff --git a/colorpicker.go b/colorpicker.go index bd554bc..f8a998b 100644 --- a/colorpicker.go +++ b/colorpicker.go @@ -6,7 +6,7 @@ import "git.tebibyte.media/tomo/tomo/input" import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/objects/layouts" -import icolor "git.tebibyte.media/tomo/objects/internal/color" +import "git.tebibyte.media/sashakoshka/goutil/image/color" var _ tomo.Object = new(HSVAColorPicker) @@ -18,7 +18,7 @@ var _ tomo.Object = new(HSVAColorPicker) // saturation and the Y axis controls value. type HSVAColorPicker struct { box tomo.ContainerBox - value icolor.HSVA + value ucolor.HSVA pickerMap *hsvaColorPickerMap hueSlider *Slider @@ -80,7 +80,7 @@ func (this *HSVAColorPicker) Value () color.Color { // SetValue sets the color of the picker. func (this *HSVAColorPicker) SetValue (value color.Color) { if value == nil { value = color.Transparent } - this.value = icolor.HSVAModel.Convert(value).(icolor.HSVA) + this.value = ucolor.HSVAModel.Convert(value).(ucolor.HSVA) this.hueSlider.SetValue(this.value.H) this.alphaSlider.SetValue(float64(this.value.A) / 0xFFFF) this.pickerMap.Invalidate() @@ -152,7 +152,7 @@ func (this *hsvaColorPickerMap) Draw (can canvas.Canvas) { xx := x - bounds.Min.X yy := y - bounds.Min.Y - pixel := icolor.HSVA { + pixel := ucolor.HSVA { H: this.parent.value.H, S: float64(xx) / float64(bounds.Dx()), V: 1 - float64(yy) / float64(bounds.Dy()), diff --git a/go.mod b/go.mod index 39b5a6e..70c618c 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,10 @@ module git.tebibyte.media/tomo/objects -go 1.20 +go 1.21.0 -require git.tebibyte.media/tomo/tomo v0.46.1 +toolchain go1.23.1 + +require ( + git.tebibyte.media/sashakoshka/goutil v0.3.0 + git.tebibyte.media/tomo/tomo v0.46.1 +) diff --git a/go.sum b/go.sum index 69a1745..2b98e28 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ +git.tebibyte.media/sashakoshka/goutil v0.3.0 h1:dcZ/9/or7m8eTpf2B1Pu4CscplXh2INTXFartz+ExwE= +git.tebibyte.media/sashakoshka/goutil v0.3.0/go.mod h1:e1OXLa+wX7x/F8n8gyxz2hnfVCEkWzGrZNX8/k/lR/M= git.tebibyte.media/tomo/tomo v0.46.1 h1:/8fT6I9l4TK529zokrThbNDHGRvUsNgif1Zs++0PBSQ= git.tebibyte.media/tomo/tomo v0.46.1/go.mod h1:WrtilgKB1y8O2Yu7X4mYcRiqOlPR8NuUnoA/ynkQWrs= diff --git a/internal/color/color.go b/internal/color/color.go index f96b0b9..a7baec6 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -3,178 +3,6 @@ package color import "fmt" import "image/color" -// HSV represents a color with hue, saturation, and value components. Each -// component C is in range 0 <= C <= 1. -type HSV struct { - H float64 - S float64 - V float64 -} - -// HSVA is an HSV color with an added 8-bit alpha component. The alpha component -// ranges from 0x0000 (fully transparent) to 0xFFFF (opaque), and has no bearing -// on the other components. -type HSVA struct { - H float64 - S float64 - V float64 - A uint16 -} - -var ( - HSVModel color.Model = color.ModelFunc(hsvModel) - HSVAModel color.Model = color.ModelFunc(hsvaModel) -) - -func (hsv HSV) RGBA () (r, g, b, a uint32) { - // Adapted from: - // https://www.cs.rit.edu/~ncs/color/t_convert.html - - component := func (x float64) uint32 { - return uint32(float64(0xFFFF) * x) - } - - s := clamp01(hsv.S) - v := clamp01(hsv.V) - if s == 0 { - light := component(v) - return light, light, light, 0xFFFF - } - - h := clamp01(hsv.H) * 360 - sector := int(h / 60) - // otherwise when given 1.0 for H, sector would overflow to 6 - if sector > 5 { sector = 5 } - offset := (h / 60) - float64(sector) - - p := component(v * (1 - s)) - q := component(v * (1 - s * offset)) - t := component(v * (1 - s * (1 - offset))) - va := component(v) - - switch sector { - case 0: return va, t, p, 0xFFFF - case 1: return q, va, p, 0xFFFF - case 2: return p, va, t, 0xFFFF - case 3: return p, q, va, 0xFFFF - case 4: return t, p, va, 0xFFFF - default: return va, p, q, 0xFFFF - } -} - -func (hsva HSVA) RGBA () (r, g, b, a uint32) { - r, g, b, a = HSV { - H: hsva.H, - S: hsva.S, - V: hsva.V, - }.RGBA() - a = uint32(hsva.A) - // alpha premultiplication - r = (r * a) / 0xFFFF - g = (g * a) / 0xFFFF - b = (b * a) / 0xFFFF - return -} - -// Canon returns the color but with the H, S, and V fields are constrained to -// the range 0.0-1.0 -func (hsv HSV) Canon () HSV { - hsv.H = clamp01(hsv.H) - hsv.S = clamp01(hsv.S) - hsv.V = clamp01(hsv.V) - return hsv -} - -// Canon returns the color but with the H, S, and V fields are constrained to -// the range 0.0-1.0 -func (hsva HSVA) Canon () HSVA { - hsva.H = clamp01(hsva.H) - hsva.S = clamp01(hsva.S) - hsva.V = clamp01(hsva.V) - return hsva -} - -func clamp01 (x float64) float64 { - if x > 1.0 { return 1.0 } - if x < 0.0 { return 0.0 } - return x -} - -func hsvModel (c color.Color) color.Color { - switch c := c.(type) { - case HSV: return c - case HSVA: return HSV { H: c.H, S: c.S, V: c.V } - default: - r, g, b, a := c.RGBA() - // alpha unpremultiplication - r = (r / a) * 0xFFFF - g = (g / a) * 0xFFFF - b = (b / a) * 0xFFFF - return rgbToHSV(r, g, b) - } -} - -func hsvaModel (c color.Color) color.Color { - switch c := c.(type) { - case HSV: return HSVA { H: c.H, S: c.S, V: c.V, A: 0xFFFF } - case HSVA: return c - default: - r, g, b, a := c.RGBA() - hsv := rgbToHSV(r, g, b) - - return HSVA { - H: hsv.H, - S: hsv.S, - V: hsv.V, - A: uint16(a), - } - } -} - -func rgbToHSV (r, g, b uint32) HSV { - // Adapted from: - // https://www.cs.rit.edu/~ncs/color/t_convert.html - - component := func (x uint32) float64 { - return clamp01(float64(x) / 0xFFFF) - } - cr := component(r) - cg := component(g) - cb := component(b) - - var maxComponent float64 - if cr > maxComponent { maxComponent = cr } - if cg > maxComponent { maxComponent = cg } - if cb > maxComponent { maxComponent = cb } - var minComponent = 1.0 - if cr < minComponent { minComponent = cr } - if cg < minComponent { minComponent = cg } - if cb < minComponent { minComponent = cb } - - hsv := HSV { - V: maxComponent, - } - - delta := maxComponent - minComponent - if delta == 0 { - // hsva.S is undefined, so hue doesn't matter - return hsv - } - hsv.S = delta / maxComponent - - switch { - case cr == maxComponent: hsv.H = (cg - cb) / delta - case cg == maxComponent: hsv.H = 2 + (cb - cr) / delta - case cb == maxComponent: hsv.H = 4 + (cr - cg) / delta - } - - hsv.H *= 60 - if hsv.H < 0 { hsv.H += 360 } - hsv.H /= 360 - - return hsv -} - // FormatNRGBA formats an NRGBA value into a hex string. func FormatNRGBA (nrgba color.NRGBA) string { return fmt.Sprintf("%02X%02X%02X%02X", nrgba.R, nrgba.G, nrgba.B, nrgba.A)