mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
app/internal/srgb: move sRGB emulation to new internal package
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -11,18 +11,18 @@ import (
|
||||
"strings"
|
||||
|
||||
"gioui.org/app/internal/glimpl"
|
||||
"gioui.org/gpu/gl"
|
||||
"gioui.org/app/internal/srgb"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
c gl.Functions
|
||||
c *glimpl.Functions
|
||||
disp _EGLDisplay
|
||||
eglCtx *eglContext
|
||||
eglSurf _EGLSurface
|
||||
width, height int
|
||||
refreshFBO bool
|
||||
// For sRGB emulation.
|
||||
srgbFBO *gl.SRGBFBO
|
||||
srgbFBO *srgb.SRGBFBO
|
||||
}
|
||||
|
||||
type eglContext struct {
|
||||
@@ -115,7 +115,7 @@ func NewContext(disp NativeDisplayType) (*Context, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Context) Functions() gl.Functions {
|
||||
func (c *Context) Functions() *glimpl.Functions {
|
||||
return c.c
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func (c *Context) MakeCurrent() error {
|
||||
}
|
||||
if c.srgbFBO == nil {
|
||||
var err error
|
||||
c.srgbFBO, err = gl.NewSRGBFBO(c.c)
|
||||
c.srgbFBO, err = srgb.NewSRGBFBO(c.c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package srgb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"gioui.org/app/internal/glimpl"
|
||||
"gioui.org/gpu/gl"
|
||||
"gioui.org/internal/unsafe"
|
||||
)
|
||||
|
||||
// SRGBFBO implements an intermediate sRGB FBO
|
||||
// for gamma-correct rendering on platforms without
|
||||
// sRGB enabled native framebuffers.
|
||||
type SRGBFBO struct {
|
||||
c *glimpl.Functions
|
||||
width, height int
|
||||
frameBuffer gl.Framebuffer
|
||||
depthBuffer gl.Renderbuffer
|
||||
colorTex gl.Texture
|
||||
blitted bool
|
||||
quad gl.Buffer
|
||||
prog gl.Program
|
||||
es3 bool
|
||||
}
|
||||
|
||||
func NewSRGBFBO(f *glimpl.Functions) (*SRGBFBO, error) {
|
||||
var es3 bool
|
||||
glVer := f.GetString(gl.VERSION)
|
||||
ver, err := gl.ParseGLVersion(glVer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ver[0] >= 3 {
|
||||
es3 = true
|
||||
} else {
|
||||
exts := f.GetString(gl.EXTENSIONS)
|
||||
if !strings.Contains(exts, "EXT_sRGB") {
|
||||
return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB")
|
||||
}
|
||||
}
|
||||
s := &SRGBFBO{
|
||||
c: f,
|
||||
es3: es3,
|
||||
frameBuffer: f.CreateFramebuffer(),
|
||||
colorTex: f.CreateTexture(),
|
||||
depthBuffer: f.CreateRenderbuffer(),
|
||||
}
|
||||
f.BindTexture(gl.TEXTURE_2D, s.colorTex)
|
||||
f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *SRGBFBO) Blit() {
|
||||
if !s.blitted {
|
||||
prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.prog = prog
|
||||
s.c.UseProgram(prog)
|
||||
s.c.Uniform1i(gl.GetUniformLocation(s.c, prog, "tex"), 0)
|
||||
s.quad = s.c.CreateBuffer()
|
||||
s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad)
|
||||
s.c.BufferData(gl.ARRAY_BUFFER,
|
||||
unsafe.BytesView([]float32{
|
||||
-1, +1, 0, 1,
|
||||
+1, +1, 1, 1,
|
||||
-1, -1, 0, 0,
|
||||
+1, -1, 1, 0,
|
||||
}),
|
||||
gl.STATIC_DRAW)
|
||||
s.blitted = true
|
||||
}
|
||||
s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
|
||||
s.c.UseProgram(s.prog)
|
||||
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
|
||||
s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad)
|
||||
s.c.VertexAttribPointer(0 /* pos */, 2, gl.FLOAT, false, 4*4, 0)
|
||||
s.c.VertexAttribPointer(1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2)
|
||||
s.c.EnableVertexAttribArray(0)
|
||||
s.c.EnableVertexAttribArray(1)
|
||||
s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
|
||||
s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{})
|
||||
s.c.DisableVertexAttribArray(0)
|
||||
s.c.DisableVertexAttribArray(1)
|
||||
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
|
||||
s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
|
||||
s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT)
|
||||
// The Android emulator requires framebuffer 0 bound at eglSwapBuffer time.
|
||||
// Bind the sRGB framebuffer again in afterPresent.
|
||||
s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{})
|
||||
}
|
||||
|
||||
func (s *SRGBFBO) AfterPresent() {
|
||||
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
|
||||
}
|
||||
|
||||
func (s *SRGBFBO) Refresh(w, h int) error {
|
||||
s.width, s.height = w, h
|
||||
if w == 0 || h == 0 {
|
||||
return nil
|
||||
}
|
||||
s.c.BindTexture(gl.TEXTURE_2D, s.colorTex)
|
||||
if s.es3 {
|
||||
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil)
|
||||
} else /* EXT_sRGB */ {
|
||||
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, w, h, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, nil)
|
||||
}
|
||||
currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING))
|
||||
s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer)
|
||||
s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h)
|
||||
s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB)
|
||||
s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer)
|
||||
s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0)
|
||||
s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer)
|
||||
if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
|
||||
return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
|
||||
}
|
||||
|
||||
if runtime.GOOS == "js" {
|
||||
// With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8
|
||||
// texture result in twice gamma corrected colors. Using a plain RGBA
|
||||
// texture seems to work.
|
||||
s.c.ClearColor(.5, .5, .5, 1.0)
|
||||
s.c.Clear(gl.COLOR_BUFFER_BIT)
|
||||
var pixel [4]byte
|
||||
s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
|
||||
if pixel[0] == 128 { // Correct sRGB color value is ~188
|
||||
s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil)
|
||||
if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
|
||||
return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SRGBFBO) Release() {
|
||||
s.c.DeleteFramebuffer(s.frameBuffer)
|
||||
s.c.DeleteTexture(s.colorTex)
|
||||
s.c.DeleteRenderbuffer(s.depthBuffer)
|
||||
if s.blitted {
|
||||
s.c.DeleteBuffer(s.quad)
|
||||
s.c.DeleteProgram(s.prog)
|
||||
}
|
||||
s.c = nil
|
||||
}
|
||||
|
||||
const (
|
||||
blitVSrc = `
|
||||
#version 100
|
||||
|
||||
precision highp float;
|
||||
|
||||
attribute vec2 pos;
|
||||
attribute vec2 uv;
|
||||
|
||||
varying vec2 vUV;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 0, 1);
|
||||
vUV = uv;
|
||||
}
|
||||
`
|
||||
blitFSrc = `
|
||||
#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
varying vec2 vUV;
|
||||
|
||||
vec3 gamma(vec3 rgb) {
|
||||
vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
|
||||
vec3 lin = rgb * vec3(12.92);
|
||||
bvec3 cut = lessThan(rgb, vec3(0.0031308));
|
||||
return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 col = texture2D(tex, vUV);
|
||||
vec3 rgb = col.rgb;
|
||||
rgb = gamma(rgb);
|
||||
gl_FragColor = vec4(rgb, col.a);
|
||||
}
|
||||
`
|
||||
)
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
type context struct {
|
||||
owner *window
|
||||
c gl.Functions
|
||||
c *glimpl.Functions
|
||||
ctx C.CFTypeRef
|
||||
layer C.CFTypeRef
|
||||
init bool
|
||||
@@ -50,7 +50,7 @@ func newContext(w *window) (*context, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *context) Functions() gl.Functions {
|
||||
func (c *context) Functions() *glimpl.Functions {
|
||||
return c.c
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
"syscall/js"
|
||||
|
||||
"gioui.org/app/internal/glimpl"
|
||||
"gioui.org/gpu/gl"
|
||||
"gioui.org/app/internal/srgb"
|
||||
)
|
||||
|
||||
type context struct {
|
||||
ctx js.Value
|
||||
cnv js.Value
|
||||
f gl.Functions
|
||||
srgbFBO *gl.SRGBFBO
|
||||
f *glimpl.Functions
|
||||
srgbFBO *srgb.SRGBFBO
|
||||
}
|
||||
|
||||
func newContext(w *window) (*context, error) {
|
||||
@@ -45,7 +45,7 @@ func newContext(w *window) (*context, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *context) Functions() gl.Functions {
|
||||
func (c *context) Functions() *glimpl.Functions {
|
||||
return c.f
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (c *context) Unlock() {}
|
||||
func (c *context) MakeCurrent() error {
|
||||
if c.srgbFBO == nil {
|
||||
var err error
|
||||
c.srgbFBO, err = gl.NewSRGBFBO(c.f)
|
||||
c.srgbFBO, err = srgb.NewSRGBFBO(c.f)
|
||||
if err != nil {
|
||||
c.Release()
|
||||
c.srgbFBO = nil
|
||||
|
||||
@@ -6,7 +6,6 @@ package window
|
||||
|
||||
import (
|
||||
"gioui.org/app/internal/glimpl"
|
||||
"gioui.org/gpu/gl"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -19,7 +18,7 @@ import (
|
||||
import "C"
|
||||
|
||||
type context struct {
|
||||
c gl.Functions
|
||||
c *glimpl.Functions
|
||||
ctx C.CFTypeRef
|
||||
view C.CFTypeRef
|
||||
}
|
||||
@@ -41,7 +40,7 @@ func newContext(w *window) (*context, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *context) Functions() gl.Functions {
|
||||
func (c *context) Functions() *glimpl.Functions {
|
||||
return c.c
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gioui.org/gpu/gl"
|
||||
"gioui.org/app/internal/glimpl"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/unit"
|
||||
@@ -32,7 +32,7 @@ type Callbacks interface {
|
||||
}
|
||||
|
||||
type Context interface {
|
||||
Functions() gl.Functions
|
||||
Functions() *glimpl.Functions
|
||||
Present() error
|
||||
MakeCurrent() error
|
||||
Release()
|
||||
|
||||
Reference in New Issue
Block a user