~eliasnaur/gio

7c1a21ce5677e15db79cc78aeaea7a2a183f74e7 — Egon Elbre 6 months ago 7024a0e
add f32color.RGBA

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
M app/headless/backend_test.go => app/headless/backend_test.go +10 -45
@@ 9,11 9,11 @@ import (
	"image/color"
	"image/png"
	"io/ioutil"
	"math"
	"runtime"
	"testing"

	"gioui.org/gpu/backend"
	"gioui.org/internal/f32color"
	"gioui.org/internal/unsafe"
)



@@ 48,8 48,8 @@ func TestSimpleShader(t *testing.T) {
	}
	// Just off the center to catch inverted triangles.
	cx, cy := 300, 400
	shaderCol := [4]float32{.25, .55, .75, 1.0}
	if got, exp := img.RGBAAt(cx, cy), tosRGB(shaderCol); got != exp {
	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)
	}
}


@@ 94,8 94,8 @@ func TestInputShader(t *testing.T) {
		t.Errorf("got color %v, expected %v", got, clearCol)
	}
	cx, cy := 300, 400
	shaderCol := [4]float32{.25, .55, .75, 1.0}
	if got, exp := img.RGBAAt(cx, cy), tosRGB(shaderCol); got != exp {
	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)
	}
}


@@ 109,11 109,11 @@ func TestFramebuffers(t *testing.T) {
		col1 = color.RGBA{R: 0xad, G: 0xbe, B: 0xef, A: 0xde}
		col2 = color.RGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca}
	)
	fcol1, fcol2 := fromsRGB(col1), fromsRGB(col2)
	b.ClearColor(fcol1[0], fcol1[1], fcol1[2], fcol1[3])
	fcol1, fcol2 := f32color.RGBAFromSRGB(col1), f32color.RGBAFromSRGB(col2)
	b.ClearColor(fcol1.Float32())
	b.BindFramebuffer(fbo1)
	b.Clear(backend.BufferAttachmentColor)
	b.ClearColor(fcol2[0], fcol2[1], fcol2[2], fcol2[3])
	b.ClearColor(fcol2.Float32())
	b.BindFramebuffer(fbo2)
	b.Clear(backend.BufferAttachmentColor)
	img := screenshot(t, fbo1, sz)


@@ 174,8 174,8 @@ func newBackend(t *testing.T) backend.Device {
	b.BeginFrame()
	// ClearColor accepts linear RGBA colors, while 8-bit colors
	// are in the sRGB color space.
	col := fromsRGB(clearCol)
	b.ClearColor(col[0], col[1], col[2], col[3])
	col := f32color.RGBAFromSRGB(clearCol)
	b.ClearColor(col.Float32())
	t.Cleanup(func() {
		b.EndFrame()
		ctx.ReleaseCurrent()


@@ 209,38 209,3 @@ func saveImage(file string, img image.Image) error {
	}
	return ioutil.WriteFile(file, buf.Bytes(), 0666)
}

func tosRGB(col [4]float32) color.RGBA {
	for i := 0; i <= 2; i++ {
		c := col[i]
		// Use the formula from EXT_sRGB.
		switch {
		case c <= 0:
			c = 0
		case 0 < c && c < 0.0031308:
			c = 12.92 * c
		case 0.0031308 <= c && c < 1:
			c = 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
		case c >= 1:
			c = 1
		}
		col[i] = c
	}
	return color.RGBA{R: uint8(col[0]*255 + .5), G: uint8(col[1]*255 + .5), B: uint8(col[2]*255 + .5), A: uint8(col[3]*255 + .5)}
}

func fromsRGB(col color.Color) [4]float32 {
	r, g, b, a := col.RGBA()
	color := [4]float32{float32(r) / 0xffff, float32(g) / 0xffff, float32(b) / 0xffff, float32(a) / 0xffff}
	for i := 0; i <= 2; i++ {
		c := color[i]
		// Use the formula from EXT_sRGB.
		if c <= 0.04045 {
			c = c / 12.92
		} else {
			c = float32(math.Pow(float64((c+0.055)/1.055), 2.4))
		}
		color[i] = c
	}
	return color
}

M app/internal/d3d11/backend_windows.go => app/internal/d3d11/backend_windows.go +3 -2
@@ 10,6 10,7 @@ import (
	"unsafe"

	"gioui.org/gpu/backend"
	"gioui.org/internal/f32color"
	gunsafe "gioui.org/internal/unsafe"
	"golang.org/x/sys/windows"
)


@@ 26,7 27,7 @@ type Device struct {
}

type Backend struct {
	clearColor [4]float32
	clearColor f32color.RGBA
	clearDepth float32
	viewport   _D3D11_VIEWPORT
	depthState depthState


@@ 539,7 540,7 @@ func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources)
}

func (b *Backend) ClearColor(colr, colg, colb, cola float32) {
	b.clearColor = [...]float32{colr, colg, colb, cola}
	b.clearColor = f32color.RGBA{R: colr, G: colg, B: colb, A: cola}
}

func (b *Backend) Clear(buffers backend.BufferAttachments) {

M app/internal/d3d11/d3d11_windows.go => app/internal/d3d11/d3d11_windows.go +4 -3
@@ 5,9 5,10 @@ package d3d11
import (
	"fmt"
	"math"
	"syscall"
	"unsafe"

	"syscall"
	"gioui.org/internal/f32color"

	"golang.org/x/sys/windows"
)


@@ 1033,7 1034,7 @@ func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencil
	)
}

func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTargetView, color *[4]float32) {
func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTargetView, color *f32color.RGBA) {
	syscall.Syscall(
		c.vtbl.ClearRenderTargetView,
		3,


@@ 1243,7 1244,7 @@ func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) {
	)
}

func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor *[4]float32, sampleMask uint32) {
func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor *f32color.RGBA, sampleMask uint32) {
	syscall.Syscall6(
		c.vtbl.OMSetBlendState,
		4,

M gpu/gpu.go => gpu/gpu.go +10 -25
@@ 19,6 19,7 @@ import (

	"gioui.org/f32"
	"gioui.org/gpu/backend"
	"gioui.org/internal/f32color"
	"gioui.org/internal/opconst"
	"gioui.org/internal/ops"
	"gioui.org/internal/path"


@@ 54,7 55,7 @@ type drawOps struct {
	reader     ops.Reader
	cache      *resourceCache
	viewport   image.Point
	clearColor [3]float32
	clearColor f32color.RGBA
	imageOps   []imageOp
	// zimageOps are the rectangle clipped opaque images
	// that can use fast front-to-back rendering with z-test


@@ 104,7 105,7 @@ type material struct {
	material materialType
	opaque   bool
	// For materialTypeColor.
	color [4]float32
	color f32color.RGBA
	// For materialTypeTexture.
	texture  *texture
	uvScale  f32.Point


@@ 256,7 257,7 @@ type blitUniforms struct {
}

type colorUniforms struct {
	color [4]float32
	color f32color.RGBA
}

type materialType uint8


@@ 334,7 335,7 @@ func (g *GPU) BeginFrame() {
	}
	g.ctx.BindFramebuffer(g.defFBO)
	g.ctx.DepthFunc(backend.DepthFuncGreater)
	g.ctx.ClearColor(g.drawOps.clearColor[0], g.drawOps.clearColor[1], g.drawOps.clearColor[2], 1.0)
	g.ctx.ClearColor(g.drawOps.clearColor.Float32())
	g.ctx.ClearDepth(0.0)
	g.ctx.Clear(backend.BufferAttachmentColor | backend.BufferAttachmentDepth)
	g.ctx.Viewport(0, 0, viewport.X, viewport.Y)


@@ 650,7 651,7 @@ func floor(v float32) int {

func (d *drawOps) reset(cache *resourceCache, viewport image.Point) {
	d.profile = false
	d.clearColor = [3]float32{1.0, 1.0, 1.0}
	d.clearColor = f32color.RGBA{R: 1.0, G: 1.0, B: 1.0, A: 1.0}
	d.cache = cache
	d.viewport = viewport
	d.imageOps = d.imageOps[:0]


@@ 744,7 745,7 @@ loop:
				d.zimageOps = d.zimageOps[:0]
				d.imageOps = d.imageOps[:0]
				state.z = 0
				copy(d.clearColor[:], mat.color[:3])
				d.clearColor = mat.color.Opaque()
				continue
			}
			state.z++


@@ 789,8 790,8 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
	switch d.matType {
	case materialColor:
		m.material = materialColor
		m.color = gamma(d.color.RGBA())
		m.opaque = m.color[3] == 1.0
		m.color = f32color.RGBAFromSRGB(d.color)
		m.opaque = m.color.A == 1.0
	case materialTexture:
		m.material = materialTexture
		dr := boundRectF(rect.Add(off))


@@ 883,23 884,7 @@ func (r *renderer) drawOps(ops []imageOp) {
	r.ctx.SetDepthTest(false)
}

func gamma(r, g, b, a uint32) [4]float32 {
	color := [4]float32{float32(r) / 0xffff, float32(g) / 0xffff, float32(b) / 0xffff, float32(a) / 0xffff}
	// Assume that image.Uniform colors are in sRGB space. Linearize.
	for i := 0; i <= 2; i++ {
		c := color[i]
		// Use the formula from EXT_sRGB.
		if c <= 0.04045 {
			c = c / 12.92
		} else {
			c = float32(math.Pow(float64((c+0.055)/1.055), 2.4))
		}
		color[i] = c
	}
	return color
}

func (b *blitter) blit(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff f32.Point) {
func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off, uvScale, uvOff f32.Point) {
	p := b.prog[mat]
	b.ctx.BindProgram(p.prog)
	var uniforms *blitUniforms

M gpu/path.go => gpu/path.go +3 -2
@@ 11,6 11,7 @@ import (

	"gioui.org/f32"
	"gioui.org/gpu/backend"
	"gioui.org/internal/f32color"
	"gioui.org/internal/path"
	gunsafe "gioui.org/internal/unsafe"
)


@@ 337,11 338,11 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
	}
}

func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
	p.coverer.cover(z, mat, col, scale, off, uvScale, uvOff, coverScale, coverOff)
}

func (c *coverer) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
	p := c.prog[mat]
	c.ctx.BindProgram(p.prog)
	var uniforms *coverUniforms

A internal/f32color/rgba.go => internal/f32color/rgba.go +75 -0
@@ 0,0 1,75 @@
// SPDX-License-Identifier: Unlicense OR MIT

package f32color

import (
	"image/color"
	"math"
)

// RGBA is a 32 bit floating point linear space color.
type RGBA struct {
	R, G, B, A float32
}

// Array returns rgba values in a [4]float32 array.
func (rgba RGBA) Array() [4]float32 {
	return [4]float32{rgba.R, rgba.G, rgba.B, rgba.A}
}

// Float32 returns r, g, b, a values.
func (col RGBA) Float32() (r, g, b, a float32) {
	return col.R, col.G, col.B, col.A
}

// 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),
		A: uint8(col.A*255 + .5),
	}
}

// Opaque returns the color without alpha component.
func (col RGBA) Opaque() RGBA {
	col.A = 1.0
	return col
}

// RGBAFromSRGB converts color.Color to RGBA.
func RGBAFromSRGB(col color.Color) RGBA {
	r, g, b, a := col.RGBA()
	return RGBA{
		R: sRGBToLinear(float32(r) / 0xffff),
		G: sRGBToLinear(float32(g) / 0xffff),
		B: sRGBToLinear(float32(b) / 0xffff),
		A: float32(a) / 0xFFFF,
	}
}

// linearTosRGB transforms color value from linear to sRGB.
func linearTosRGB(c float32) float32 {
	// Formula from EXT_sRGB.
	switch {
	case c <= 0:
		return 0
	case 0 < c && c < 0.0031308:
		return 12.92 * c
	case 0.0031308 <= c && c < 1:
		return 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
	}

	return 1
}

// sRGBToLinear transforms color value from sRGB to linear.
func sRGBToLinear(c float32) float32 {
	// Formula from EXT_sRGB.
	if c <= 0.04045 {
		return c / 12.92
	} else {
		return float32(math.Pow(float64((c+0.055)/1.055), 2.4))
	}
}