github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/gl/srgb.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package gl
     4  
     5  import (
     6  	"fmt"
     7  	"runtime"
     8  	"strings"
     9  )
    10  
    11  // SRGBFBO implements an intermediate sRGB FBO
    12  // for gamma-correct rendering on platforms without
    13  // sRGB enabled native framebuffers.
    14  type SRGBFBO struct {
    15  	c             *Functions
    16  	width, height int
    17  	frameBuffer   Framebuffer
    18  	depthBuffer   Renderbuffer
    19  	colorTex      Texture
    20  	blitted       bool
    21  	quad          Buffer
    22  	prog          Program
    23  	es3           bool
    24  }
    25  
    26  func NewSRGBFBO(f *Functions) (*SRGBFBO, error) {
    27  	var es3 bool
    28  	glVer := f.GetString(VERSION)
    29  	ver, err := ParseGLVersion(glVer)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	if ver[0] >= 3 {
    34  		es3 = true
    35  	} else {
    36  		exts := f.GetString(EXTENSIONS)
    37  		if !strings.Contains(exts, "EXT_sRGB") {
    38  			return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB")
    39  		}
    40  	}
    41  	s := &SRGBFBO{
    42  		c:           f,
    43  		es3:         es3,
    44  		frameBuffer: f.CreateFramebuffer(),
    45  		colorTex:    f.CreateTexture(),
    46  		depthBuffer: f.CreateRenderbuffer(),
    47  	}
    48  	f.BindTexture(TEXTURE_2D, s.colorTex)
    49  	f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
    50  	f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
    51  	f.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST)
    52  	f.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST)
    53  	return s, nil
    54  }
    55  
    56  func (s *SRGBFBO) Blit() {
    57  	if !s.blitted {
    58  		prog, err := CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
    59  		if err != nil {
    60  			panic(err)
    61  		}
    62  		s.prog = prog
    63  		s.c.UseProgram(prog)
    64  		s.c.Uniform1i(GetUniformLocation(s.c, prog, "tex"), 0)
    65  		s.quad = s.c.CreateBuffer()
    66  		s.c.BindBuffer(ARRAY_BUFFER, s.quad)
    67  		s.c.BufferData(ARRAY_BUFFER,
    68  			BytesView([]float32{
    69  				-1, +1, 0, 1,
    70  				+1, +1, 1, 1,
    71  				-1, -1, 0, 0,
    72  				+1, -1, 1, 0,
    73  			}),
    74  			STATIC_DRAW)
    75  		s.blitted = true
    76  	}
    77  	s.c.BindFramebuffer(FRAMEBUFFER, Framebuffer{})
    78  	s.c.ClearColor(1, 0, 1, 1)
    79  	s.c.Clear(COLOR_BUFFER_BIT)
    80  	s.c.UseProgram(s.prog)
    81  	s.c.BindTexture(TEXTURE_2D, s.colorTex)
    82  	s.c.BindBuffer(ARRAY_BUFFER, s.quad)
    83  	s.c.VertexAttribPointer(0 /* pos */, 2, FLOAT, false, 4*4, 0)
    84  	s.c.VertexAttribPointer(1 /* uv */, 2, FLOAT, false, 4*4, 4*2)
    85  	s.c.EnableVertexAttribArray(0)
    86  	s.c.EnableVertexAttribArray(1)
    87  	s.c.DrawArrays(TRIANGLE_STRIP, 0, 4)
    88  	s.c.BindTexture(TEXTURE_2D, Texture{})
    89  	s.c.DisableVertexAttribArray(0)
    90  	s.c.DisableVertexAttribArray(1)
    91  	s.c.BindFramebuffer(FRAMEBUFFER, s.frameBuffer)
    92  	s.c.InvalidateFramebuffer(FRAMEBUFFER, COLOR_ATTACHMENT0)
    93  	s.c.InvalidateFramebuffer(FRAMEBUFFER, DEPTH_ATTACHMENT)
    94  	// The Android emulator requires framebuffer 0 bound at eglSwapBuffer time.
    95  	// Bind the sRGB framebuffer again in afterPresent.
    96  	s.c.BindFramebuffer(FRAMEBUFFER, Framebuffer{})
    97  }
    98  
    99  func (s *SRGBFBO) AfterPresent() {
   100  	s.c.BindFramebuffer(FRAMEBUFFER, s.frameBuffer)
   101  }
   102  
   103  func (s *SRGBFBO) Refresh(w, h int) error {
   104  	s.width, s.height = w, h
   105  	if w == 0 || h == 0 {
   106  		return nil
   107  	}
   108  	s.c.BindTexture(TEXTURE_2D, s.colorTex)
   109  	if s.es3 {
   110  		s.c.TexImage2D(TEXTURE_2D, 0, SRGB8_ALPHA8, w, h, RGBA, UNSIGNED_BYTE, nil)
   111  	} else /* EXT_sRGB */ {
   112  		s.c.TexImage2D(TEXTURE_2D, 0, SRGB_ALPHA_EXT, w, h, SRGB_ALPHA_EXT, UNSIGNED_BYTE, nil)
   113  	}
   114  	currentRB := Renderbuffer(s.c.GetBinding(RENDERBUFFER_BINDING))
   115  	s.c.BindRenderbuffer(RENDERBUFFER, s.depthBuffer)
   116  	s.c.RenderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT16, w, h)
   117  	s.c.BindRenderbuffer(RENDERBUFFER, currentRB)
   118  	s.c.BindFramebuffer(FRAMEBUFFER, s.frameBuffer)
   119  	s.c.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, s.colorTex, 0)
   120  	s.c.FramebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, s.depthBuffer)
   121  	if st := s.c.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE {
   122  		return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
   123  	}
   124  
   125  	if runtime.GOOS == "js" {
   126  		// With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8
   127  		// texture result in twice gamma corrected colors. Using a plain RGBA
   128  		// texture seems to work.
   129  		s.c.ClearColor(.5, .5, .5, 1.0)
   130  		s.c.Clear(COLOR_BUFFER_BIT)
   131  		var pixel [4]byte
   132  		s.c.ReadPixels(0, 0, 1, 1, RGBA, UNSIGNED_BYTE, pixel[:])
   133  		if pixel[0] == 128 { // Correct sRGB color value is ~188
   134  			s.c.TexImage2D(TEXTURE_2D, 0, RGBA, w, h, RGBA, UNSIGNED_BYTE, nil)
   135  			if st := s.c.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE {
   136  				return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError())
   137  			}
   138  		}
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func (s *SRGBFBO) Release() {
   145  	s.c.DeleteFramebuffer(s.frameBuffer)
   146  	s.c.DeleteTexture(s.colorTex)
   147  	s.c.DeleteRenderbuffer(s.depthBuffer)
   148  	if s.blitted {
   149  		s.c.DeleteBuffer(s.quad)
   150  		s.c.DeleteProgram(s.prog)
   151  	}
   152  	s.c = nil
   153  }
   154  
   155  const (
   156  	blitVSrc = `
   157  #version 100
   158  
   159  precision highp float;
   160  
   161  attribute vec2 pos;
   162  attribute vec2 uv;
   163  
   164  varying vec2 vUV;
   165  
   166  void main() {
   167      gl_Position = vec4(pos, 0, 1);
   168      vUV = uv;
   169  }
   170  `
   171  	blitFSrc = `
   172  #version 100
   173  
   174  precision mediump float;
   175  
   176  uniform sampler2D tex;
   177  varying vec2 vUV;
   178  
   179  vec3 gamma(vec3 rgb) {
   180  	vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
   181  	vec3 lin = rgb * vec3(12.92);
   182  	bvec3 cut = lessThan(rgb, vec3(0.0031308));
   183  	return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b);
   184  }
   185  
   186  void main() {
   187      vec4 col = texture2D(tex, vUV);
   188  	vec3 rgb = col.rgb;
   189  	rgb = gamma(rgb);
   190  	gl_FragColor = vec4(rgb, col.a);
   191  }
   192  `
   193  )