From 21ef492cc9dfd9161e9fc57df25684865cd1c847 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Wed, 18 Nov 2020 20:21:26 +0200 Subject: [PATCH] all: use color.NRGBA in public API color.RGBA has two problems with regards to using it. First the color values need to be premultiplied, whereas most APIs have non-premultiplied values. This is mainly to preserve color components with low alpha values. Second there are two ways to premultiply with sRGB. One is to premultiply after sRGB conversion, the other is before. This makes using the API more confusing. Using color.NRGBA in sRGB makes it align with CSS.e Signed-off-by: Egon Elbre --- app/headless/backend_test.go | 39 +++++++------- app/headless/headless_test.go | 29 +++++----- gpu/gpu.go | 26 ++++----- internal/f32color/rgba.go | 76 +++++++++++++++++++-------- internal/rendertest/bench_test.go | 6 +-- internal/rendertest/clip_test.go | 28 +++++----- internal/rendertest/render_test.go | 48 ++++++++--------- internal/rendertest/transform_test.go | 16 +++--- internal/rendertest/util_test.go | 10 ++++ op/paint/doc.go | 2 +- op/paint/paint.go | 14 ++--- widget/border.go | 2 +- widget/icon.go | 9 ++-- widget/material/button.go | 12 ++--- widget/material/checkable.go | 4 +- widget/material/doc.go | 2 +- widget/material/editor.go | 4 +- widget/material/label.go | 2 +- widget/material/loader.go | 2 +- widget/material/progressbar.go | 4 +- widget/material/slider.go | 2 +- widget/material/switch.go | 6 +-- widget/material/theme.go | 14 ++--- 23 files changed, 200 insertions(+), 157 deletions(-) diff --git a/app/headless/backend_test.go b/app/headless/backend_test.go index 4c652e1b..891ad2aa 100644 --- a/app/headless/backend_test.go +++ b/app/headless/backend_test.go @@ -19,15 +19,16 @@ import ( var dumpImages = flag.Bool("saveimages", false, "save test images") -var clearCol = color.RGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe} +var clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe} +var clearColExpect = f32color.NRGBAToRGBA(clearCol) func TestFramebufferClear(t *testing.T) { b := newBackend(t) sz := image.Point{X: 800, Y: 600} fbo := setupFBO(t, b, sz) img := screenshot(t, fbo, sz) - if got := img.RGBAAt(0, 0); got != clearCol { - t.Errorf("got color %v, expected %v", got, clearCol) + if got := img.RGBAAt(0, 0); got != clearColExpect { + t.Errorf("got color %v, expected %v", got, clearColExpect) } } @@ -43,14 +44,14 @@ func TestSimpleShader(t *testing.T) { b.BindProgram(p) b.DrawArrays(backend.DrawModeTriangles, 0, 3) img := screenshot(t, fbo, sz) - if got := img.RGBAAt(0, 0); got != clearCol { - t.Errorf("got color %v, expected %v", got, clearCol) + if got := img.RGBAAt(0, 0); got != clearColExpect { + t.Errorf("got color %v, expected %v", got, clearColExpect) } // Just off the center to catch inverted triangles. cx, cy := 300, 400 shaderCol := f32color.RGBA{R: .25, G: .55, B: .75, A: 1.0} - if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != exp { - t.Errorf("got color %v, expected %v", got, exp) + if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != f32color.NRGBAToRGBA(exp) { + t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(exp)) } } @@ -90,13 +91,13 @@ func TestInputShader(t *testing.T) { b.BindInputLayout(layout) b.DrawArrays(backend.DrawModeTriangles, 0, 3) img := screenshot(t, fbo, sz) - if got := img.RGBAAt(0, 0); got != clearCol { - t.Errorf("got color %v, expected %v", got, clearCol) + if got := img.RGBAAt(0, 0); got != clearColExpect { + t.Errorf("got color %v, expected %v", got, clearColExpect) } cx, cy := 300, 400 shaderCol := f32color.RGBA{R: .25, G: .55, B: .75, A: 1.0} - if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != exp { - t.Errorf("got color %v, expected %v", got, exp) + if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != f32color.NRGBAToRGBA(exp) { + t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(exp)) } } @@ -106,21 +107,21 @@ func TestFramebuffers(t *testing.T) { fbo1 := newFBO(t, b, sz) fbo2 := newFBO(t, b, sz) var ( - col1 = color.RGBA{R: 0xad, G: 0xbe, B: 0xef, A: 0xde} - col2 = color.RGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca} + col1 = color.NRGBA{R: 0xad, G: 0xbe, B: 0xef, A: 0xde} + col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca} ) - fcol1, fcol2 := f32color.RGBAFromSRGB(col1), f32color.RGBAFromSRGB(col2) + fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2) b.BindFramebuffer(fbo1) b.Clear(fcol1.Float32()) b.BindFramebuffer(fbo2) b.Clear(fcol2.Float32()) img := screenshot(t, fbo1, sz) - if got := img.RGBAAt(0, 0); got != col1 { - t.Errorf("got color %v, expected %v", got, col1) + if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) { + t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1)) } img = screenshot(t, fbo2, sz) - if got := img.RGBAAt(0, 0); got != col2 { - t.Errorf("got color %v, expected %v", got, col2) + if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col2) { + t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col2)) } } @@ -129,7 +130,7 @@ func setupFBO(t *testing.T, b backend.Device, size image.Point) backend.Framebuf b.BindFramebuffer(fbo) // ClearColor accepts linear RGBA colors, while 8-bit colors // are in the sRGB color space. - col := f32color.RGBAFromSRGB(clearCol) + col := f32color.LinearFromSRGB(clearCol) b.Clear(col.Float32()) b.ClearDepth(0.0) b.Viewport(0, 0, size.X, size.Y) diff --git a/app/headless/headless_test.go b/app/headless/headless_test.go index 294feb83..516ea0b9 100644 --- a/app/headless/headless_test.go +++ b/app/headless/headless_test.go @@ -8,6 +8,7 @@ import ( "testing" "gioui.org/f32" + "gioui.org/internal/f32color" "gioui.org/op" "gioui.org/op/clip" "gioui.org/op/paint" @@ -18,7 +19,7 @@ func TestHeadless(t *testing.T) { defer release() sz := w.size - col := color.RGBA{A: 0xff, R: 0xca, G: 0xfe} + col := color.NRGBA{A: 0xff, R: 0xca, G: 0xfe} var ops op.Ops paint.ColorOp{Color: col}.Add(&ops) // Paint only part of the screen to avoid the glClear optimization. @@ -34,8 +35,8 @@ func TestHeadless(t *testing.T) { if isz := img.Bounds().Size(); isz != sz { t.Errorf("got %v screenshot, expected %v", isz, sz) } - if got := img.RGBAAt(0, 0); got != col { - t.Errorf("got color %v, expected %v", got, col) + if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col) { + t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col)) } } @@ -43,8 +44,8 @@ func TestClipping(t *testing.T) { w, release := newTestWindow(t) defer release() - col := color.RGBA{A: 0xff, R: 0xca, G: 0xfe} - col2 := color.RGBA{A: 0xff, R: 0x00, G: 0xfe} + col := color.NRGBA{A: 0xff, R: 0xca, G: 0xfe} + col2 := color.NRGBA{A: 0xff, R: 0x00, G: 0xfe} var ops op.Ops paint.ColorOp{Color: col}.Add(&ops) clip.RRect{ @@ -77,10 +78,10 @@ func TestClipping(t *testing.T) { t.Fatal(err) } } - bg := color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff} + bg := color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff} tests := []struct { x, y int - color color.RGBA + color color.NRGBA }{ {120, 120, col}, {130, 130, col2}, @@ -88,8 +89,8 @@ func TestClipping(t *testing.T) { {230, 230, bg}, } for _, test := range tests { - if got := img.RGBAAt(test.x, test.y); got != test.color { - t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, test.color) + if got := img.RGBAAt(test.x, test.y); got != f32color.NRGBAToRGBA(test.color) { + t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, f32color.NRGBAToRGBA(test.color)) } } } @@ -99,9 +100,9 @@ func TestDepth(t *testing.T) { defer release() var ops op.Ops - blue := color.RGBA{B: 0xFF, A: 0xFF} + blue := color.NRGBA{B: 0xFF, A: 0xFF} paint.FillShape(&ops, blue, clip.Rect(image.Rect(0, 0, 50, 100)).Op()) - red := color.RGBA{R: 0xFF, A: 0xFF} + red := color.NRGBA{R: 0xFF, A: 0xFF} paint.FillShape(&ops, red, clip.Rect(image.Rect(0, 0, 100, 50)).Op()) if err := w.Frame(&ops); err != nil { t.Fatal(err) @@ -118,15 +119,15 @@ func TestDepth(t *testing.T) { } tests := []struct { x, y int - color color.RGBA + color color.NRGBA }{ {25, 25, red}, {75, 25, red}, {25, 75, blue}, } for _, test := range tests { - if got := img.RGBAAt(test.x, test.y); got != test.color { - t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, test.color) + if got := img.RGBAAt(test.x, test.y); got != f32color.NRGBAToRGBA(test.color) { + t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, f32color.NRGBAToRGBA(test.color)) } } } diff --git a/gpu/gpu.go b/gpu/gpu.go index 6637bd64..5eb484bd 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -78,13 +78,13 @@ type drawState struct { // Current paint.ImageOp image imageOpData // Current paint.ColorOp, if any. - color color.RGBA + color color.NRGBA // Current paint.LinearGradientOp. stop1 f32.Point stop2 f32.Point - color1 color.RGBA - color2 color.RGBA + color1 color.NRGBA + color2 color.NRGBA } type pathOp struct { @@ -138,9 +138,9 @@ type imageOpData struct { type linearGradientOpData struct { stop1 f32.Point - color1 color.RGBA + color1 color.NRGBA stop2 f32.Point - color2 color.RGBA + color2 color.NRGBA } func (op *clipOp) decode(data []byte) { @@ -183,11 +183,11 @@ func decodeImageOp(data []byte, refs []interface{}) imageOpData { } } -func decodeColorOp(data []byte) color.RGBA { +func decodeColorOp(data []byte) color.NRGBA { if opconst.OpType(data[0]) != opconst.TypeColor { panic("invalid op") } - return color.RGBA{ + return color.NRGBA{ R: data[1], G: data[2], B: data[3], @@ -209,13 +209,13 @@ func decodeLinearGradientOp(data []byte) linearGradientOpData { X: math.Float32frombits(bo.Uint32(data[9:])), Y: math.Float32frombits(bo.Uint32(data[13:])), }, - color1: color.RGBA{ + color1: color.NRGBA{ R: data[17+0], G: data[17+1], B: data[17+2], A: data[17+3], }, - color2: color.RGBA{ + color2: color.NRGBA{ R: data[21+0], G: data[21+1], B: data[21+2], @@ -749,7 +749,7 @@ func (d *drawOps) collect(cache *resourceCache, root *op.Ops, viewport image.Poi state := drawState{ clip: clip, rect: true, - color: color.RGBA{A: 0xff}, + color: color.NRGBA{A: 0xff}, } d.collectOps(&d.reader, state) } @@ -930,13 +930,13 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3 switch d.matType { case materialColor: m.material = materialColor - m.color = f32color.RGBAFromSRGB(d.color) + m.color = f32color.LinearFromSRGB(d.color) m.opaque = m.color.A == 1.0 case materialLinearGradient: m.material = materialLinearGradient - m.color1 = f32color.RGBAFromSRGB(d.color1) - m.color2 = f32color.RGBAFromSRGB(d.color2) + m.color1 = f32color.LinearFromSRGB(d.color1) + m.color2 = f32color.LinearFromSRGB(d.color2) m.opaque = m.color1.A == 1.0 && m.color2.A == 1.0 m.uvTrans = trans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2)) diff --git a/internal/f32color/rgba.go b/internal/f32color/rgba.go index dcc5d1d0..c72f50ef 100644 --- a/internal/f32color/rgba.go +++ b/internal/f32color/rgba.go @@ -7,7 +7,7 @@ import ( "math" ) -// RGBA is a 32 bit floating point linear space color. +// RGBA is a 32 bit floating point linear premultiplied color space. type RGBA struct { R, G, B, A float32 } @@ -23,11 +23,14 @@ func (col RGBA) Float32() (r, g, b, a float32) { } // SRGBA converts from linear to sRGB color space. -func (col RGBA) SRGB() color.RGBA { - return color.RGBA{ - R: uint8(linearTosRGB(col.R)*255 + .5), - G: uint8(linearTosRGB(col.G)*255 + .5), - B: uint8(linearTosRGB(col.B)*255 + .5), +func (col RGBA) SRGB() color.NRGBA { + if col.A == 0 { + return color.NRGBA{} + } + return color.NRGBA{ + R: uint8(linearTosRGB(col.R/col.A)*255 + .5), + G: uint8(linearTosRGB(col.G/col.A)*255 + .5), + B: uint8(linearTosRGB(col.B/col.A)*255 + .5), A: uint8(col.A*255 + .5), } } @@ -38,17 +41,50 @@ func (col RGBA) Opaque() RGBA { return col } -// RGBAFromSRGB converts from SRGBA to RGBA. -func RGBAFromSRGB(col color.RGBA) RGBA { - r, g, b, a := col.RGBA() +// LinearFromSRGB converts from SRGBA to RGBA. +func LinearFromSRGB(col color.NRGBA) RGBA { + af := float32(col.A) / 0xFF return RGBA{ - R: sRGBToLinear(float32(r) / 0xffff), - G: sRGBToLinear(float32(g) / 0xffff), - B: sRGBToLinear(float32(b) / 0xffff), - A: float32(a) / 0xFFFF, + R: sRGBToLinear(float32(col.R)/0xff) * af, + G: sRGBToLinear(float32(col.G)/0xff) * af, + B: sRGBToLinear(float32(col.B)/0xff) * af, + A: af, } } +// NRGBAToRGBA converts from non-premultiplied sRGB color to premultiplied sRGB color. +// +// Each component in the result is `sRGBToLinear(c * alpha)`, where `c` +// is the linear color. +func NRGBAToRGBA(col color.NRGBA) color.RGBA { + if col.A == 0xFF { + return color.RGBA(col) + } + c := LinearFromSRGB(col) + return color.RGBA{ + R: uint8(linearTosRGB(c.R)*255 + .5), + G: uint8(linearTosRGB(c.G)*255 + .5), + B: uint8(linearTosRGB(c.B)*255 + .5), + A: col.A, + } +} + +// RGBAToNRGBA converts from premultiplied sRGB color to non-premultiplied sRGB color. +func RGBAToNRGBA(col color.RGBA) color.NRGBA { + if col.A == 0xFF { + return color.NRGBA(col) + } + + linear := RGBA{ + R: sRGBToLinear(float32(col.R) / 0xff), + G: sRGBToLinear(float32(col.G) / 0xff), + B: sRGBToLinear(float32(col.B) / 0xff), + A: float32(col.A) / 0xff, + } + + return linear.SRGB() +} + // linearTosRGB transforms color value from linear to sRGB. func linearTosRGB(c float32) float32 { // Formula from EXT_sRGB. @@ -74,14 +110,8 @@ func sRGBToLinear(c float32) float32 { } } -// MulAlpha scales all color components by alpha/255. -func MulAlpha(c color.RGBA, alpha uint8) color.RGBA { - // TODO: Optimize. This is pretty slow. - a := float32(alpha) / 255. - rgba := RGBAFromSRGB(c) - rgba.A *= a - rgba.R *= a - rgba.G *= a - rgba.B *= a - return rgba.SRGB() +// MulAlpha applies the alpha to the color. +func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA { + c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF) + return c } diff --git a/internal/rendertest/bench_test.go b/internal/rendertest/bench_test.go index ca63eff1..b9efb424 100644 --- a/internal/rendertest/bench_test.go +++ b/internal/rendertest/bench_test.go @@ -157,7 +157,7 @@ func draw1000Circles(gtx layout.Context) { op.Offset(f32.Pt(float32(x*10), 0)).Add(ops) for y := 0; y < 10; y++ { paint.FillShape(ops, - color.RGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, + color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, clip.RRect{Rect: f32.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Op(ops), ) op.Offset(f32.Pt(0, float32(100))).Add(ops) @@ -179,7 +179,7 @@ func draw1000CirclesInstanced(gtx layout.Context) { op.Offset(f32.Pt(float32(x*10), 0)).Add(ops) for y := 0; y < 10; y++ { pi := op.Push(ops) - paint.ColorOp{Color: color.RGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops) + paint.ColorOp{Color: color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops) c.Add(ops) pi.Pop() op.Offset(f32.Pt(0, float32(100))).Add(ops) @@ -208,7 +208,7 @@ func drawIndividualShapes(gtx layout.Context, th *material.Theme) chan op.CallOp op.Offset(f32.Pt(float32(x*50), 0)).Add(ops) for y := 0; y < 9; y++ { paint.FillShape(ops, - color.RGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, + color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, clip.RRect{Rect: f32.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Op(ops), ) op.Offset(f32.Pt(0, float32(50))).Add(ops) diff --git a/internal/rendertest/clip_test.go b/internal/rendertest/clip_test.go index f7805347..7746156d 100644 --- a/internal/rendertest/clip_test.go +++ b/internal/rendertest/clip_test.go @@ -14,7 +14,7 @@ import ( func TestPaintRect(t *testing.T) { run(t, func(o *op.Ops) { - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) }, func(r result) { r.expect(0, 0, colornames.Red) r.expect(49, 0, colornames.Red) @@ -26,7 +26,7 @@ func TestPaintRect(t *testing.T) { func TestPaintClippedRect(t *testing.T) { run(t, func(o *op.Ops) { clip.RRect{Rect: f32.Rect(25, 25, 60, 60)}.Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(24, 35, colornames.White) @@ -41,7 +41,7 @@ func TestPaintClippedCirle(t *testing.T) { r := float32(10) clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Add(o) clip.Rect(image.Rect(0, 0, 30, 50)).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(21, 21, colornames.White) r.expect(25, 30, colornames.Red) @@ -70,7 +70,7 @@ func TestPaintArc(t *testing.T) { p.Line(f32.Pt(-50, 0)) p.Outline().Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 128, 128)).Op()) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(0, 25, colornames.Red) @@ -123,7 +123,7 @@ func TestStrokedPathBevelFlat(t *testing.T) { p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(10, 50, colornames.Red) @@ -150,7 +150,7 @@ func TestStrokedPathBevelRound(t *testing.T) { p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(10, 50, colornames.Red) @@ -177,7 +177,7 @@ func TestStrokedPathBevelSquare(t *testing.T) { p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(10, 50, colornames.Red) @@ -204,7 +204,7 @@ func TestStrokedPathRoundRound(t *testing.T) { p.Quad(f32.Pt(-10, -10), f32.Pt(-30, 30)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(10, 50, colornames.Red) @@ -232,7 +232,7 @@ func TestStrokedPathFlatMiter(t *testing.T) { p.Line(f32.Pt(50, 0)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) } { @@ -246,7 +246,7 @@ func TestStrokedPathFlatMiter(t *testing.T) { p.Line(f32.Pt(50, 0)) p.Stroke(2, clip.StrokeStyle{}).Add(o) - paint.Fill(o, colornames.Black) + paint.Fill(o, black) } }, func(r result) { @@ -277,7 +277,7 @@ func TestStrokedPathFlatMiterInf(t *testing.T) { p.Line(f32.Pt(50, 0)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) } { @@ -291,7 +291,7 @@ func TestStrokedPathFlatMiterInf(t *testing.T) { p.Line(f32.Pt(50, 0)) p.Stroke(2, clip.StrokeStyle{}).Add(o) - paint.Fill(o, colornames.Black) + paint.Fill(o, black) } }, func(r result) { @@ -312,7 +312,7 @@ func TestStrokedPathZeroWidth(t *testing.T) { p.Line(f32.Pt(50, 0)) p.Stroke(width, sty).Add(o) - paint.Fill(o, colornames.Black) + paint.Fill(o, black) } { @@ -322,7 +322,7 @@ func TestStrokedPathZeroWidth(t *testing.T) { p.Line(f32.Pt(30, 0)) p.Stroke(0, sty).Add(o) // width=0, disable stroke - paint.Fill(o, colornames.Red) + paint.Fill(o, red) } }, func(r result) { diff --git a/internal/rendertest/render_test.go b/internal/rendertest/render_test.go index 4c2d95cb..75576150 100644 --- a/internal/rendertest/render_test.go +++ b/internal/rendertest/render_test.go @@ -25,12 +25,12 @@ func TestTransformMacro(t *testing.T) { // render the first Stacked item m1 := op.Record(o) dr := image.Rect(0, 0, 128, 50) - paint.FillShape(o, colornames.Black, clip.Rect(dr).Op()) + paint.FillShape(o, black, clip.Rect(dr).Op()) c1 := m1.Stop() // Render the second stacked item m2 := op.Record(o) - paint.ColorOp{Color: colornames.Red}.Add(o) + paint.ColorOp{Color: red}.Add(o) // Simulate a draw text call stack := op.Push(o) op.Offset(f32.Pt(0, 10)).Add(o) @@ -62,7 +62,7 @@ func TestTransformMacro(t *testing.T) { func TestRepeatedPaintsZ(t *testing.T) { run(t, func(o *op.Ops) { // Draw a rectangle - paint.FillShape(o, colornames.Black, clip.Rect(image.Rect(0, 0, 128, 50)).Op()) + paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 128, 50)).Op()) builder := clip.Path{} builder.Begin(o) @@ -72,7 +72,7 @@ func TestRepeatedPaintsZ(t *testing.T) { builder.Line(f32.Pt(-10, 0)) builder.Line(f32.Pt(0, -10)) builder.Outline().Add(o) - paint.Fill(o, colornames.Red) + paint.Fill(o, red) }, func(r result) { r.expect(5, 5, colornames.Red) r.expect(11, 15, colornames.Black) @@ -86,11 +86,11 @@ func TestNoClipFromPaint(t *testing.T) { run(t, func(o *op.Ops) { a := f32.Affine2D{}.Rotate(f32.Pt(20, 20), math.Pi/4) op.Affine(a).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(10, 10, 30, 30)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(10, 10, 30, 30)).Op()) a = f32.Affine2D{}.Rotate(f32.Pt(20, 20), -math.Pi/4) op.Affine(a).Add(o) - paint.FillShape(o, colornames.Black, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) + paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) }, func(r result) { r.expect(1, 1, colornames.Black) r.expect(20, 20, colornames.Black) @@ -195,16 +195,16 @@ func TestNegativeOverlaps(t *testing.T) { } type Gradient struct { - From, To color.RGBA + From, To color.NRGBA } var gradients = []Gradient{ - {From: color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}}, - {From: color.RGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}}, - {From: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}}, - {From: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}}, - {From: color.RGBA{R: 0x19, G: 0xFF, B: 0xFF, A: 0xFF}, To: color.RGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}}, - {From: color.RGBA{R: 0xFF, G: 0xFF, B: 0x19, A: 0xFF}, To: color.RGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}}, + {From: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}, To: color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}}, + {From: color.NRGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}, To: color.NRGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}}, + {From: color.NRGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}, To: color.NRGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}}, + {From: color.NRGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}, To: color.NRGBA{R: 0x19, G: 0xFF, B: 0x19, A: 0xFF}}, + {From: color.NRGBA{R: 0x19, G: 0xFF, B: 0xFF, A: 0xFF}, To: color.NRGBA{R: 0xFF, G: 0x19, B: 0x19, A: 0xFF}}, + {From: color.NRGBA{R: 0xFF, G: 0xFF, B: 0x19, A: 0xFF}, To: color.NRGBA{R: 0x19, G: 0x19, B: 0xFF, A: 0xFF}}, } func TestLinearGradient(t *testing.T) { @@ -236,11 +236,11 @@ func TestLinearGradient(t *testing.T) { }, func(r result) { gr := pixelAligned for _, g := range gradients { - from := f32color.RGBAFromSRGB(g.From) - to := f32color.RGBAFromSRGB(g.To) + from := f32color.LinearFromSRGB(g.From) + to := f32color.LinearFromSRGB(g.To) for _, p := range samples { exp := lerp(from, to, float32(p)/float32(r.img.Bounds().Dx()-1)) - r.expect(p, int(gr.Min.Y+gradienth/2), exp.SRGB()) + r.expect(p, int(gr.Min.Y+gradienth/2), f32color.NRGBAToRGBA(exp.SRGB())) } gr = gr.Add(f32.Pt(0, gradienth)) } @@ -251,9 +251,9 @@ func TestLinearGradientAngled(t *testing.T) { run(t, func(ops *op.Ops) { paint.LinearGradientOp{ Stop1: f32.Pt(64, 64), - Color1: colornames.Black, + Color1: black, Stop2: f32.Pt(0, 0), - Color2: colornames.Red, + Color2: red, }.Add(ops) st := op.Push(ops) clip.Rect(image.Rect(0, 0, 64, 64)).Add(ops) @@ -262,9 +262,9 @@ func TestLinearGradientAngled(t *testing.T) { paint.LinearGradientOp{ Stop1: f32.Pt(64, 64), - Color1: colornames.White, + Color1: white, Stop2: f32.Pt(128, 0), - Color2: colornames.Green, + Color2: green, }.Add(ops) st = op.Push(ops) clip.Rect(image.Rect(64, 0, 128, 64)).Add(ops) @@ -273,9 +273,9 @@ func TestLinearGradientAngled(t *testing.T) { paint.LinearGradientOp{ Stop1: f32.Pt(64, 64), - Color1: colornames.Black, + Color1: black, Stop2: f32.Pt(128, 128), - Color2: colornames.Blue, + Color2: blue, }.Add(ops) st = op.Push(ops) clip.Rect(image.Rect(64, 64, 128, 128)).Add(ops) @@ -284,9 +284,9 @@ func TestLinearGradientAngled(t *testing.T) { paint.LinearGradientOp{ Stop1: f32.Pt(64, 64), - Color1: colornames.White, + Color1: white, Stop2: f32.Pt(0, 128), - Color2: colornames.Magenta, + Color2: magenta, }.Add(ops) st = op.Push(ops) clip.Rect(image.Rect(0, 64, 64, 128)).Add(ops) diff --git a/internal/rendertest/transform_test.go b/internal/rendertest/transform_test.go index bbebd9a4..27e49918 100644 --- a/internal/rendertest/transform_test.go +++ b/internal/rendertest/transform_test.go @@ -15,7 +15,7 @@ import ( func TestPaintOffset(t *testing.T) { run(t, func(o *op.Ops) { op.Offset(f32.Pt(10, 20)).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(59, 30, colornames.Red) @@ -28,7 +28,7 @@ func TestPaintRotate(t *testing.T) { run(t, func(o *op.Ops) { a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/8) op.Affine(a).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(20, 20, 60, 60)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op()) }, func(r result) { r.expect(40, 40, colornames.Red) r.expect(50, 19, colornames.Red) @@ -41,7 +41,7 @@ func TestPaintShear(t *testing.T) { run(t, func(o *op.Ops) { a := f32.Affine2D{}.Shear(f32.Point{}, math.Pi/4, 0) op.Affine(a).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 40, 40)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op()) }, func(r result) { r.expect(10, 30, colornames.White) }) @@ -51,7 +51,7 @@ func TestClipPaintOffset(t *testing.T) { run(t, func(o *op.Ops) { clip.RRect{Rect: f32.Rect(10, 10, 30, 30)}.Add(o) op.Offset(f32.Pt(20, 20)).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(19, 19, colornames.White) @@ -64,7 +64,7 @@ func TestClipOffset(t *testing.T) { run(t, func(o *op.Ops) { op.Offset(f32.Pt(20, 20)).Add(o) clip.RRect{Rect: f32.Rect(10, 10, 30, 30)}.Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) }, func(r result) { r.expect(0, 0, colornames.White) r.expect(29, 29, colornames.White) @@ -79,7 +79,7 @@ func TestClipScale(t *testing.T) { a := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10)) op.Affine(a).Add(o) clip.RRect{Rect: f32.Rect(10, 10, 20, 20)}.Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op()) }, func(r result) { r.expect(19+10, 19+10, colornames.White) r.expect(20+10, 20+10, colornames.Red) @@ -92,7 +92,7 @@ func TestClipRotate(t *testing.T) { run(t, func(o *op.Ops) { op.Affine(f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/4)).Add(o) clip.RRect{Rect: f32.Rect(30, 30, 50, 50)}.Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 40, 100, 100)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op()) }, func(r result) { r.expect(39, 39, colornames.White) r.expect(41, 41, colornames.Red) @@ -188,7 +188,7 @@ func TestTransformOrder(t *testing.T) { c := f32.Affine2D{}.Offset(f32.Pt(-10, -10)).Scale(f32.Point{}, f32.Pt(0.5, 0.5)) op.Affine(c).Add(o) - paint.FillShape(o, colornames.Red, clip.Rect(image.Rect(0, 0, 20, 20)).Op()) + paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op()) }, func(r result) { // centered and with radius 40 r.expect(64-41, 64, colornames.White) diff --git a/internal/rendertest/util_test.go b/internal/rendertest/util_test.go index d6b9f704..6568aceb 100644 --- a/internal/rendertest/util_test.go +++ b/internal/rendertest/util_test.go @@ -15,6 +15,7 @@ import ( "gioui.org/app/headless" "gioui.org/f32" + "gioui.org/internal/f32color" "gioui.org/op" "gioui.org/op/paint" "golang.org/x/image/colornames" @@ -25,6 +26,15 @@ var ( squares paint.ImageOp ) +var ( + red = f32color.RGBAToNRGBA(colornames.Red) + green = f32color.RGBAToNRGBA(colornames.Green) + blue = f32color.RGBAToNRGBA(colornames.Blue) + magenta = f32color.RGBAToNRGBA(colornames.Magenta) + black = f32color.RGBAToNRGBA(colornames.Black) + white = f32color.RGBAToNRGBA(colornames.White) +) + func init() { // build the texture we use for testing size := 512 diff --git a/op/paint/doc.go b/op/paint/doc.go index ae69faca..79054ab6 100644 --- a/op/paint/doc.go +++ b/op/paint/doc.go @@ -9,6 +9,6 @@ taking the current transformation into account. The current brush is set by either a ColorOp for a constant color, or ImageOp for an image, or LinearGradientOp for gradients. -All color.RGBA values are in the sRGB color space. +All color.NRGBA values are in the sRGB color space. */ package paint diff --git a/op/paint/paint.go b/op/paint/paint.go index 73ff8734..291348f1 100644 --- a/op/paint/paint.go +++ b/op/paint/paint.go @@ -21,7 +21,7 @@ import ( // See NewImageOp for details. type ImageOp struct { uniform bool - color color.RGBA + color color.NRGBA src *image.RGBA // handle is a key to uniquely identify this ImageOp @@ -31,16 +31,16 @@ type ImageOp struct { // ColorOp sets the brush to a constant color. type ColorOp struct { - Color color.RGBA + Color color.NRGBA } // LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and // ending at stop2 with color2. type LinearGradientOp struct { Stop1 f32.Point - Color1 color.RGBA + Color1 color.NRGBA Stop2 f32.Point - Color2 color.RGBA + Color2 color.NRGBA } // PaintOp fills fills the current clip area with the current brush. @@ -58,7 +58,7 @@ type PaintOp struct { func NewImageOp(src image.Image) ImageOp { switch src := src.(type) { case *image.Uniform: - col := color.RGBAModel.Convert(src.C).(color.RGBA) + col := color.NRGBAModel.Convert(src.C).(color.NRGBA) return ImageOp{ uniform: true, color: col, @@ -138,7 +138,7 @@ func (d PaintOp) Add(o *op.Ops) { } // FillShape fills the clip shape with a color. -func FillShape(ops *op.Ops, c color.RGBA, shape clip.Op) { +func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) { defer op.Push(ops).Pop() shape.Add(ops) Fill(ops, c) @@ -148,7 +148,7 @@ func FillShape(ops *op.Ops, c color.RGBA, shape clip.Op) { // is intended to be used with a clip.Op already in place to limit // the painted area. Use FillShape unless you need to paint several // times within the same clip.Op. -func Fill(ops *op.Ops, c color.RGBA) { +func Fill(ops *op.Ops, c color.NRGBA) { defer op.Push(ops).Pop() ColorOp{Color: c}.Add(ops) PaintOp{}.Add(ops) diff --git a/widget/border.go b/widget/border.go index e8bae150..da201447 100644 --- a/widget/border.go +++ b/widget/border.go @@ -15,7 +15,7 @@ import ( // Border lays out a widget and draws a border inside it. type Border struct { - Color color.RGBA + Color color.NRGBA CornerRadius unit.Value Width unit.Value } diff --git a/widget/icon.go b/widget/icon.go index 17ef8af9..846c6222 100644 --- a/widget/icon.go +++ b/widget/icon.go @@ -7,6 +7,7 @@ import ( "image/color" "image/draw" + "gioui.org/internal/f32color" "gioui.org/layout" "gioui.org/op/paint" "gioui.org/unit" @@ -14,12 +15,12 @@ import ( ) type Icon struct { - Color color.RGBA + Color color.NRGBA src []byte // Cached values. op paint.ImageOp imgSize int - imgColor color.RGBA + imgColor color.NRGBA } // NewIcon returns a new Icon from IconVG data. @@ -28,7 +29,7 @@ func NewIcon(data []byte) (*Icon, error) { if err != nil { return nil, err } - return &Icon{src: data, Color: color.RGBA{A: 0xff}}, nil + return &Icon{src: data, Color: color.NRGBA{A: 0xff}}, nil } func (ic *Icon) Layout(gtx layout.Context, sz unit.Value) layout.Dimensions { @@ -49,7 +50,7 @@ func (ic *Icon) image(sz int) paint.ImageOp { img := image.NewRGBA(image.Rectangle{Max: image.Point{X: sz, Y: int(float32(sz) * dy / dx)}}) var ico iconvg.Rasterizer ico.SetDstImage(img, img.Bounds(), draw.Src) - m.Palette[0] = ic.Color + m.Palette[0] = f32color.NRGBAToRGBA(ic.Color) iconvg.Decode(&ico, ic.src, &iconvg.DecodeOptions{ Palette: &m.Palette, }) diff --git a/widget/material/button.go b/widget/material/button.go index 18b9c1cd..f5e6dce3 100644 --- a/widget/material/button.go +++ b/widget/material/button.go @@ -22,10 +22,10 @@ import ( type ButtonStyle struct { Text string // Color is the text color. - Color color.RGBA + Color color.NRGBA Font text.Font TextSize unit.Value - Background color.RGBA + Background color.NRGBA CornerRadius unit.Value Inset layout.Inset Button *widget.Clickable @@ -33,15 +33,15 @@ type ButtonStyle struct { } type ButtonLayoutStyle struct { - Background color.RGBA + Background color.NRGBA CornerRadius unit.Value Button *widget.Clickable } type IconButtonStyle struct { - Background color.RGBA + Background color.NRGBA // Color is the icon color. - Color color.RGBA + Color color.NRGBA Icon *widget.Icon // Size is the icon size. Size unit.Value @@ -272,7 +272,7 @@ func drawInk(gtx layout.Context, c widget.Press) { const col = 0.8 ba, bc := byte(alpha*0xff), byte(col*0xff) defer op.Push(gtx.Ops).Pop() - rgba := f32color.MulAlpha(color.RGBA{A: 0xff, R: bc, G: bc, B: bc}, ba) + rgba := f32color.MulAlpha(color.NRGBA{A: 0xff, R: bc, G: bc, B: bc}, ba) ink := paint.ColorOp{Color: rgba} ink.Add(gtx.Ops) rr := size * .5 diff --git a/widget/material/checkable.go b/widget/material/checkable.go index 59033895..617d3201 100644 --- a/widget/material/checkable.go +++ b/widget/material/checkable.go @@ -17,10 +17,10 @@ import ( type checkable struct { Label string - Color color.RGBA + Color color.NRGBA Font text.Font TextSize unit.Value - IconColor color.RGBA + IconColor color.NRGBA Size unit.Value shaper text.Shaper checkedStateIcon *widget.Icon diff --git a/widget/material/doc.go b/widget/material/doc.go index d7679d58..715f5a07 100644 --- a/widget/material/doc.go +++ b/widget/material/doc.go @@ -36,7 +36,7 @@ // Theme-global parameters: For changing the look of all widgets drawn with a // particular theme, adjust the `Theme` fields: // -// theme.Color.Primary = color.RGBA{...} +// theme.Color.Primary = color.NRGBA{...} // // Widget-local parameters: For changing the look of a particular widget, // adjust the widget specific theme object: diff --git a/widget/material/editor.go b/widget/material/editor.go index 7e0d187c..ae6805d5 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -18,11 +18,11 @@ type EditorStyle struct { Font text.Font TextSize unit.Value // Color is the text color. - Color color.RGBA + Color color.NRGBA // Hint contains the text displayed when the editor is empty. Hint string // HintColor is the color of hint text. - HintColor color.RGBA + HintColor color.NRGBA Editor *widget.Editor shaper text.Shaper diff --git a/widget/material/label.go b/widget/material/label.go index c475c31d..256dcfd5 100644 --- a/widget/material/label.go +++ b/widget/material/label.go @@ -16,7 +16,7 @@ type LabelStyle struct { // Face defines the text style. Font text.Font // Color is the text color. - Color color.RGBA + Color color.NRGBA // Alignment specify the text alignment. Alignment text.Alignment // MaxLines limits the number of lines. Zero means no limit. diff --git a/widget/material/loader.go b/widget/material/loader.go index f80842f1..48a4b6f2 100644 --- a/widget/material/loader.go +++ b/widget/material/loader.go @@ -17,7 +17,7 @@ import ( ) type LoaderStyle struct { - Color color.RGBA + Color color.NRGBA } func Loader(th *Theme) LoaderStyle { diff --git a/widget/material/progressbar.go b/widget/material/progressbar.go index a5fa9fd8..5cba9baf 100644 --- a/widget/material/progressbar.go +++ b/widget/material/progressbar.go @@ -15,7 +15,7 @@ import ( ) type ProgressBarStyle struct { - Color color.RGBA + Color color.NRGBA Progress int } @@ -27,7 +27,7 @@ func ProgressBar(th *Theme, progress int) ProgressBarStyle { } func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions { - shader := func(width float32, color color.RGBA) layout.Dimensions { + shader := func(width float32, color color.NRGBA) layout.Dimensions { maxHeight := unit.Dp(4) rr := float32(gtx.Px(unit.Dp(2))) diff --git a/widget/material/slider.go b/widget/material/slider.go index f0239829..4756c23c 100644 --- a/widget/material/slider.go +++ b/widget/material/slider.go @@ -28,7 +28,7 @@ func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle { type SliderStyle struct { Min, Max float32 - Color color.RGBA + Color color.NRGBA Float *widget.Float } diff --git a/widget/material/switch.go b/widget/material/switch.go index 000091c4..93c22a17 100644 --- a/widget/material/switch.go +++ b/widget/material/switch.go @@ -19,8 +19,8 @@ import ( type SwitchStyle struct { Color struct { - Enabled color.RGBA - Disabled color.RGBA + Enabled color.NRGBA + Disabled color.NRGBA } Switch *widget.Bool } @@ -124,7 +124,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions { return layout.Dimensions{Size: dims} } -func drawDisc(ops *op.Ops, sz float32, col color.RGBA) { +func drawDisc(ops *op.Ops, sz float32, col color.NRGBA) { defer op.Push(ops).Pop() rr := sz / 2 r := f32.Rectangle{Max: f32.Point{X: sz, Y: sz}} diff --git a/widget/material/theme.go b/widget/material/theme.go index 40412ab1..b55af125 100644 --- a/widget/material/theme.go +++ b/widget/material/theme.go @@ -14,10 +14,10 @@ import ( type Theme struct { Shaper text.Shaper Color struct { - Primary color.RGBA - Text color.RGBA - Hint color.RGBA - InvText color.RGBA + Primary color.NRGBA + Text color.NRGBA + Hint color.NRGBA + InvText color.NRGBA } TextSize unit.Value Icon struct { @@ -53,10 +53,10 @@ func mustIcon(ic *widget.Icon, err error) *widget.Icon { return ic } -func rgb(c uint32) color.RGBA { +func rgb(c uint32) color.NRGBA { return argb(0xff000000 | c) } -func argb(c uint32) color.RGBA { - return color.RGBA{A: uint8(c >> 24), R: uint8(c >> 16), G: uint8(c >> 8), B: uint8(c)} +func argb(c uint32) color.NRGBA { + return color.NRGBA{A: uint8(c >> 24), R: uint8(c >> 16), G: uint8(c >> 8), B: uint8(c)} }