func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { f, err := c.dst.createFramebufferIfNeeded(context) if err != nil { return err } if err := f.setAsViewport(context); err != nil { return err } context.BlendFunc(c.mode) n := c.quadsNum() if n == 0 { return nil } _, h := c.dst.Size() proj := f.projectionMatrix(h) p := &programContext{ state: &theOpenGLState, program: theOpenGLState.programTexture, context: context, projectionMatrix: proj, texture: c.src.texture.native, colorM: c.color, } if err := p.begin(); err != nil { return err } // TODO: We should call glBindBuffer here? // The buffer is already bound at begin() but it is counterintuitive. context.DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes) return nil }
func shader(c *opengl.Context, id shaderId) string { str := shaders[id] if !c.GlslHighpSupported() { str = strings.Replace(str, "highp ", "", -1) str = strings.Replace(str, "lowp ", "", -1) } return str }
func newFramebufferFromTexture(context *opengl.Context, texture *texture) (*framebuffer, error) { native, err := context.NewFramebuffer(opengl.Texture(texture.native)) if err != nil { return nil, err } return &framebuffer{ native: native, }, nil }
func (c *disposeCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { if c.target.framebuffer != nil { context.DeleteFramebuffer(c.target.framebuffer.native) } if c.target.texture != nil { context.DeleteTexture(c.target.texture.native) } return nil }
func (i *Image) Pixels(context *opengl.Context) ([]uint8, error) { // Flush the enqueued commands so that pixels are certainly read. if err := theCommandQueue.Flush(context); err != nil { return nil, err } f, err := i.createFramebufferIfNeeded(context) if err != nil { return nil, err } return context.FramebufferPixels(f.native, i.width, i.height) }
func (a *arrayBufferLayout) enable(c *opengl.Context, program opengl.Program) { for _, p := range a.parts { c.EnableVertexAttribArray(program, p.name) } total := a.totalBytes() offset := 0 for _, p := range a.parts { c.VertexAttribPointer(program, p.name, p.num, p.dataType, p.normalize, total, offset) offset += p.dataType.SizeInBytes() * p.num } }
func (c *newScreenFramebufferImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { if c.width < 1 { return errors.New("graphics: width must be equal or more than 1.") } if c.height < 1 { return errors.New("graphics: height must be equal or more than 1.") } f := &framebuffer{ native: context.ScreenFramebuffer(), flipY: true, } c.result.framebuffer = f return nil }
func (c *replacePixelsCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { f, err := c.dst.createFramebufferIfNeeded(context) if err != nil { return err } if err := f.setAsViewport(context); err != nil { return err } // Filling with non black or white color is required here for glTexSubImage2D. // Very mysterious but this actually works (Issue #186). // This is needed even after fixing a shader bug at f537378f2a6a8ef56e1acf1c03034967b77c7b51. if err := context.FillFramebuffer(0, 0, 0.5, 1); err != nil { return err } // This is necessary on Android. We can't call glClear just before glTexSubImage2D without // glFlush. glTexSubImage2D didn't work without this hack at least on Nexus 5x (#211). // This also happens when a fillCommand precedes a replacePixelsCommand. // TODO: Can we have a better way like optimizing commands? context.Flush() if err := context.BindTexture(c.dst.texture.native); err != nil { return err } context.TexSubImage2D(c.pixels, c.dst.width, c.dst.height) return nil }
func (c *fillCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { f, err := c.dst.createFramebufferIfNeeded(context) if err != nil { return err } if err := f.setAsViewport(context); err != nil { return err } cr, cg, cb, ca := c.color.R, c.color.G, c.color.B, c.color.A const max = math.MaxUint8 r := float64(cr) / max g := float64(cg) / max b := float64(cb) / max a := float64(ca) / max return context.FillFramebuffer(r, g, b, a) }
func (c *newImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { w := NextPowerOf2Int(c.width) h := NextPowerOf2Int(c.height) if w < 1 { return errors.New("graphics: width must be equal or more than 1.") } if h < 1 { return errors.New("graphics: height must be equal or more than 1.") } native, err := context.NewTexture(w, h, nil, c.filter) if err != nil { return err } c.result.texture = &texture{ native: native, } return nil }
func (c *newImageFromImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error { origSize := c.img.Bounds().Size() if origSize.X < 1 { return errors.New("graphics: width must be equal or more than 1.") } if origSize.Y < 1 { return errors.New("graphics: height must be equal or more than 1.") } w, h := c.img.Bounds().Size().X, c.img.Bounds().Size().Y if c.img.Bounds() != image.Rect(0, 0, NextPowerOf2Int(w), NextPowerOf2Int(h)) { panic(fmt.Sprintf("graphics: invalid image bounds: %v", c.img.Bounds())) } native, err := context.NewTexture(w, h, c.img.Pix, c.filter) if err != nil { return err } c.result.texture = &texture{ native: native, } return nil }
func (q *commandQueue) Flush(context *opengl.Context) error { q.m.Lock() defer q.m.Unlock() // glViewport must be called at least at every frame on iOS. context.ResetViewportSize() for _, g := range q.commandGroups() { n := 0 for _, c := range g { switch c := c.(type) { case *drawImageCommand: n += len(c.vertices) } } vertices := make([]float32, 0, n) for _, c := range g { switch c := c.(type) { case *drawImageCommand: vertices = append(vertices, c.vertices...) } } if 0 < len(vertices) { context.BufferSubData(opengl.ArrayBuffer, vertices) } // NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far. // Let's use them to compare to len(quads) in the future. if maxQuads < len(vertices)*opengl.Float.SizeInBytes()/QuadVertexSizeInBytes() { return fmt.Errorf("len(quads) must be equal to or less than %d", maxQuads) } numc := len(g) indexOffsetInBytes := 0 for _, c := range g { if err := c.Exec(context, indexOffsetInBytes); err != nil { return err } if c, ok := c.(*drawImageCommand); ok { n := len(c.vertices) * opengl.Float.SizeInBytes() / QuadVertexSizeInBytes() indexOffsetInBytes += 6 * n * 2 } } if 0 < numc { // Call glFlush to prevent black flicking (especially on Android (#226) and iOS). context.Flush() } } q.commands = []command{} return nil }
func (i *Image) IsInvalidated(context *opengl.Context) bool { return !context.IsTexture(i.texture.native) }
func (f *framebuffer) setAsViewport(context *opengl.Context) error { width := viewportSize height := viewportSize return context.SetViewport(f.native, width, height) }
func (a *arrayBufferLayout) disable(c *opengl.Context, program opengl.Program) { // TODO: Disabling should be done in reversed order? for _, p := range a.parts { c.DisableVertexAttribArray(program, p.name) } }
func (a *arrayBufferLayout) newArrayBuffer(c *opengl.Context) opengl.Buffer { return c.NewBuffer(opengl.ArrayBuffer, a.totalBytes()*4*maxQuads, opengl.DynamicDraw) }
func (s *openGLState) reset(context *opengl.Context) error { if err := context.Reset(); err != nil { return err } s.lastProgram = zeroProgram s.lastProjectionMatrix = nil s.lastColorMatrix = nil s.lastColorMatrixTranslation = nil if s.arrayBuffer != zeroBuffer { context.DeleteBuffer(s.arrayBuffer) } if s.indexBufferQuads != zeroBuffer { context.DeleteBuffer(s.indexBufferQuads) } if s.programTexture != zeroProgram { context.DeleteProgram(s.programTexture) } shaderVertexModelviewNative, err := context.NewShader(opengl.VertexShader, shader(context, shaderVertexModelview)) if err != nil { panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err)) } defer context.DeleteShader(shaderVertexModelviewNative) shaderFragmentTextureNative, err := context.NewShader(opengl.FragmentShader, shader(context, shaderFragmentTexture)) if err != nil { panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err)) } defer context.DeleteShader(shaderFragmentTextureNative) s.programTexture, err = context.NewProgram([]opengl.Shader{ shaderVertexModelviewNative, shaderFragmentTextureNative, }) if err != nil { return err } s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context) indices := make([]uint16, 6*maxQuads) for i := uint16(0); i < maxQuads; i++ { indices[6*i+0] = 4*i + 0 indices[6*i+1] = 4*i + 1 indices[6*i+2] = 4*i + 2 indices[6*i+3] = 4*i + 1 indices[6*i+4] = 4*i + 2 indices[6*i+5] = 4*i + 3 } s.indexBufferQuads = context.NewBuffer(opengl.ElementArrayBuffer, indices, opengl.StaticDraw) return nil }