mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
Compare commits
15 Commits
147aef89b8
...
d168f149d9
Author | SHA1 | Date | |
---|---|---|---|
|
d168f149d9 | ||
|
cd90f083bc | ||
|
d15b12b4e5 | ||
|
359e7b8597 | ||
|
d1b9216ee1 | ||
|
f02e9fd4d0 | ||
|
f44640778d | ||
|
66a4b20bda | ||
|
7f9d997175 | ||
|
c90d02f8d4 | ||
|
2b7d20e7da | ||
|
d69bb04a56 | ||
|
5f61cf00e5 | ||
|
7f01f98200 | ||
|
fe887e2565 |
@ -35,7 +35,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
inconsolataFace = text.NewStdFace(inconsolata.Bold8x16)
|
||||
inconsolataFace = text.NewGoXFace(inconsolata.Bold8x16)
|
||||
)
|
||||
|
||||
// mode is a blend mode with description.
|
||||
|
@ -35,7 +35,7 @@ const (
|
||||
screenHeight = 240
|
||||
)
|
||||
|
||||
var fontFace = text.NewStdFace(bitmapfont.Face)
|
||||
var fontFace = text.NewGoXFace(bitmapfont.Face)
|
||||
|
||||
var keyboardImage *ebiten.Image
|
||||
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
var fontFace = text.NewStdFace(bitmapfont.FaceEA)
|
||||
var fontFace = text.NewGoXFace(bitmapfont.FaceEA)
|
||||
|
||||
const (
|
||||
screenWidth = 640
|
||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module github.com/hajimehoshi/ebiten/v2
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/ebitengine/gomobile v0.0.0-20240223151600-9f1d75a9f41c
|
||||
github.com/ebitengine/gomobile v0.0.0-20240318151619-0eadfb33c201
|
||||
github.com/ebitengine/hideconsole v1.0.0
|
||||
github.com/ebitengine/oto/v3 v3.2.0-alpha.4
|
||||
github.com/ebitengine/purego v0.7.0-alpha.3
|
||||
|
4
go.sum
4
go.sum
@ -1,5 +1,5 @@
|
||||
github.com/ebitengine/gomobile v0.0.0-20240223151600-9f1d75a9f41c h1:fIrdax248gvTVn/QL+U0taS0Fs9SVKKSpXxiibMs6p4=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240223151600-9f1d75a9f41c/go.mod h1:8SdR2+sMMmYsei+c0ZW+AmJx2W6dPeSixWWSKRkJppA=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240318151619-0eadfb33c201 h1:QlcxUcnQjv62kMxsh0NAQpwE0P454ec2i82DxmthMeI=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240318151619-0eadfb33c201/go.mod h1:8SdR2+sMMmYsei+c0ZW+AmJx2W6dPeSixWWSKRkJppA=
|
||||
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
|
||||
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
|
||||
github.com/ebitengine/oto/v3 v3.2.0-alpha.4 h1:aaUdcbEDUV1oErHDv/Cd0IAjQaQPChZuvO8Cn/kQHE8=
|
||||
|
165
internal/shader/delayed.go
Normal file
165
internal/shader/delayed.go
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2024 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
gconstant "go/constant"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
type delayedTypeValidator interface {
|
||||
Validate(t shaderir.Type) (shaderir.Type, bool)
|
||||
IsValidated() (shaderir.Type, bool)
|
||||
Error() string
|
||||
}
|
||||
|
||||
func isArgDefaultTypeInt(f shaderir.BuiltinFunc) bool {
|
||||
return f == shaderir.IntF || f == shaderir.IVec2F || f == shaderir.IVec3F || f == shaderir.IVec4F
|
||||
}
|
||||
|
||||
func isIntType(t shaderir.Type) bool {
|
||||
return t.Main == shaderir.Int || t.IsIntVector()
|
||||
}
|
||||
|
||||
func (cs *compileState) ValidateDefaultTypesForExpr(block *block, expr shaderir.Expr, t shaderir.Type) shaderir.Type {
|
||||
if check, ok := cs.delayedTypeCheks[expr.Ast]; ok {
|
||||
if resT, ok := check.IsValidated(); ok {
|
||||
return resT
|
||||
}
|
||||
resT, ok := check.Validate(t)
|
||||
if !ok {
|
||||
return shaderir.Type{Main: shaderir.None}
|
||||
}
|
||||
return resT
|
||||
}
|
||||
|
||||
switch expr.Type {
|
||||
case shaderir.LocalVariable:
|
||||
return block.vars[expr.Index].typ
|
||||
|
||||
case shaderir.Binary:
|
||||
left := expr.Exprs[0]
|
||||
right := expr.Exprs[1]
|
||||
|
||||
leftType := cs.ValidateDefaultTypesForExpr(block, left, t)
|
||||
rightType := cs.ValidateDefaultTypesForExpr(block, right, t)
|
||||
|
||||
// Usure about top-level type, try to validate by neighbour type
|
||||
// The same work is done twice. Can it be optimized?
|
||||
if t.Main == shaderir.None {
|
||||
cs.ValidateDefaultTypesForExpr(block, left, rightType)
|
||||
cs.ValidateDefaultTypesForExpr(block, right, leftType)
|
||||
}
|
||||
case shaderir.Call:
|
||||
fun := expr.Exprs[0]
|
||||
if fun.Type == shaderir.BuiltinFuncExpr {
|
||||
if isArgDefaultTypeInt(fun.BuiltinFunc) {
|
||||
for _, e := range expr.Exprs[1:] {
|
||||
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.Int})
|
||||
}
|
||||
return shaderir.Type{Main: shaderir.Int}
|
||||
}
|
||||
|
||||
for _, e := range expr.Exprs[1:] {
|
||||
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.Float})
|
||||
}
|
||||
return shaderir.Type{Main: shaderir.Float}
|
||||
}
|
||||
|
||||
if fun.Type == shaderir.FunctionExpr {
|
||||
args := cs.funcs[fun.Index].ir.InParams
|
||||
|
||||
for i, e := range expr.Exprs[1:] {
|
||||
cs.ValidateDefaultTypesForExpr(block, e, args[i])
|
||||
}
|
||||
|
||||
retT := cs.funcs[fun.Index].ir.Return
|
||||
|
||||
return retT
|
||||
}
|
||||
}
|
||||
|
||||
return shaderir.Type{Main: shaderir.None}
|
||||
}
|
||||
|
||||
func (cs *compileState) ValidateDefaultTypes(block *block, stmt shaderir.Stmt) {
|
||||
switch stmt.Type {
|
||||
case shaderir.Assign:
|
||||
left := stmt.Exprs[0]
|
||||
right := stmt.Exprs[1]
|
||||
if left.Type == shaderir.LocalVariable {
|
||||
varType := block.vars[left.Index].typ
|
||||
// Type is not explicitly specified
|
||||
if stmt.IsTypeGuessed {
|
||||
varType = shaderir.Type{Main: shaderir.None}
|
||||
}
|
||||
cs.ValidateDefaultTypesForExpr(block, right, varType)
|
||||
}
|
||||
case shaderir.ExprStmt:
|
||||
for _, e := range stmt.Exprs {
|
||||
cs.ValidateDefaultTypesForExpr(block, e, shaderir.Type{Main: shaderir.None})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type delayedShiftValidator struct {
|
||||
shiftType shaderir.Op
|
||||
value gconstant.Value
|
||||
validated bool
|
||||
closestUnknown bool
|
||||
failed bool
|
||||
}
|
||||
|
||||
func (d *delayedShiftValidator) IsValidated() (shaderir.Type, bool) {
|
||||
if d.failed {
|
||||
return shaderir.Type{}, false
|
||||
}
|
||||
if d.validated {
|
||||
return shaderir.Type{Main: shaderir.Int}, true
|
||||
}
|
||||
// If only matched with None
|
||||
if d.closestUnknown {
|
||||
// Was it originally represented by an int constant?
|
||||
if d.value.Kind() == gconstant.Int {
|
||||
return shaderir.Type{Main: shaderir.Int}, true
|
||||
}
|
||||
}
|
||||
return shaderir.Type{}, false
|
||||
}
|
||||
|
||||
func (d *delayedShiftValidator) Validate(t shaderir.Type) (shaderir.Type, bool) {
|
||||
if d.validated {
|
||||
return shaderir.Type{Main: shaderir.Int}, true
|
||||
}
|
||||
if isIntType(t) {
|
||||
d.validated = true
|
||||
return shaderir.Type{Main: shaderir.Int}, true
|
||||
}
|
||||
if t.Main == shaderir.None {
|
||||
d.closestUnknown = true
|
||||
return t, true
|
||||
}
|
||||
return shaderir.Type{Main: shaderir.None}, false
|
||||
}
|
||||
|
||||
func (d *delayedShiftValidator) Error() string {
|
||||
st := "left shift"
|
||||
if d.shiftType == shaderir.RightShift {
|
||||
st = "right shift"
|
||||
}
|
||||
return fmt.Sprintf("left operand for %s should be int", st)
|
||||
}
|
@ -36,7 +36,7 @@ func canTruncateToFloat(v gconstant.Value) bool {
|
||||
|
||||
var textureVariableRe = regexp.MustCompile(`\A__t(\d+)\z`)
|
||||
|
||||
func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, markLocalVariableUsed bool) ([]shaderir.Expr, []shaderir.Type, []shaderir.Stmt, bool) {
|
||||
func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, markLocalVariableUsed bool) (rexpr []shaderir.Expr, rtype []shaderir.Type, rstmt []shaderir.Stmt, ok bool) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.BasicLit:
|
||||
switch e.Kind {
|
||||
@ -105,7 +105,14 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
||||
}
|
||||
|
||||
// Resolve untyped constants.
|
||||
l, r, ok := shaderir.ResolveUntypedConstsForBinaryOp(lhs[0].Const, rhs[0].Const, lhst, rhst)
|
||||
var l gconstant.Value
|
||||
var r gconstant.Value
|
||||
origLvalue := lhs[0].Const
|
||||
if op2 == shaderir.LeftShift || op2 == shaderir.RightShift {
|
||||
l, r, ok = shaderir.ResolveUntypedConstsForBitShiftOp(lhs[0].Const, rhs[0].Const, lhst, rhst)
|
||||
} else {
|
||||
l, r, ok = shaderir.ResolveUntypedConstsForBinaryOp(lhs[0].Const, rhs[0].Const, lhst, rhst)
|
||||
}
|
||||
if !ok {
|
||||
// TODO: Show a better type name for untyped constants.
|
||||
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), op, rhst.String()))
|
||||
@ -113,27 +120,49 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
||||
}
|
||||
lhs[0].Const, rhs[0].Const = l, r
|
||||
|
||||
// If either is typed, resolve the other type.
|
||||
// If both are untyped, keep them untyped.
|
||||
if lhst.Main != shaderir.None || rhst.Main != shaderir.None {
|
||||
if lhs[0].Const != nil {
|
||||
switch lhs[0].Const.Kind() {
|
||||
case gconstant.Float:
|
||||
lhst = shaderir.Type{Main: shaderir.Float}
|
||||
case gconstant.Int:
|
||||
if op2 == shaderir.LeftShift || op2 == shaderir.RightShift {
|
||||
if !(lhst.Main == shaderir.None && rhst.Main == shaderir.None) {
|
||||
// If both are const
|
||||
if rhs[0].Const != nil && (rhst.Main == shaderir.None || lhs[0].Const != nil) {
|
||||
rhst = shaderir.Type{Main: shaderir.Int}
|
||||
}
|
||||
|
||||
// If left is untyped const
|
||||
if lhst.Main == shaderir.None && lhs[0].Const != nil {
|
||||
lhst = shaderir.Type{Main: shaderir.Int}
|
||||
case gconstant.Bool:
|
||||
lhst = shaderir.Type{Main: shaderir.Bool}
|
||||
// Left should be implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
|
||||
if rhs[0].Const == nil {
|
||||
defer func() {
|
||||
if ok {
|
||||
cs.addDelayedTypeCheck(expr, &delayedShiftValidator{value: origLvalue})
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
if rhs[0].Const != nil {
|
||||
switch rhs[0].Const.Kind() {
|
||||
case gconstant.Float:
|
||||
rhst = shaderir.Type{Main: shaderir.Float}
|
||||
case gconstant.Int:
|
||||
rhst = shaderir.Type{Main: shaderir.Int}
|
||||
case gconstant.Bool:
|
||||
rhst = shaderir.Type{Main: shaderir.Bool}
|
||||
} else {
|
||||
// If either is typed, resolve the other type.
|
||||
// If both are untyped, keep them untyped.
|
||||
if lhst.Main != shaderir.None || rhst.Main != shaderir.None {
|
||||
if lhs[0].Const != nil {
|
||||
switch lhs[0].Const.Kind() {
|
||||
case gconstant.Float:
|
||||
lhst = shaderir.Type{Main: shaderir.Float}
|
||||
case gconstant.Int:
|
||||
lhst = shaderir.Type{Main: shaderir.Int}
|
||||
case gconstant.Bool:
|
||||
lhst = shaderir.Type{Main: shaderir.Bool}
|
||||
}
|
||||
}
|
||||
if rhs[0].Const != nil {
|
||||
switch rhs[0].Const.Kind() {
|
||||
case gconstant.Float:
|
||||
rhst = shaderir.Type{Main: shaderir.Float}
|
||||
case gconstant.Int:
|
||||
rhst = shaderir.Type{Main: shaderir.Int}
|
||||
case gconstant.Bool:
|
||||
rhst = shaderir.Type{Main: shaderir.Bool}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,6 +182,13 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
||||
v = gconstant.MakeBool(b)
|
||||
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
|
||||
v = gconstant.MakeBool(gconstant.Compare(lhs[0].Const, op, rhs[0].Const))
|
||||
case token.SHL, token.SHR:
|
||||
shift, ok := gconstant.Int64Val(rhs[0].Const)
|
||||
if !ok {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("unexpected %s type for: %s", rhs[0].Const.String(), e.Op))
|
||||
} else {
|
||||
v = gconstant.Shift(lhs[0].Const, op, uint(shift))
|
||||
}
|
||||
default:
|
||||
v = gconstant.BinaryOp(lhs[0].Const, op, rhs[0].Const)
|
||||
}
|
||||
@ -169,6 +205,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
||||
{
|
||||
Type: shaderir.Binary,
|
||||
Op: op2,
|
||||
Ast: expr,
|
||||
Exprs: []shaderir.Expr{lhs[0], rhs[0]},
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
|
@ -61,6 +61,8 @@ type compileState struct {
|
||||
|
||||
varyingParsed bool
|
||||
|
||||
delayedTypeCheks map[ast.Expr]delayedTypeValidator
|
||||
|
||||
errs []string
|
||||
}
|
||||
|
||||
@ -82,6 +84,13 @@ func (cs *compileState) findUniformVariable(name string) (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (cs *compileState) addDelayedTypeCheck(at ast.Expr, check delayedTypeValidator) {
|
||||
if cs.delayedTypeCheks == nil {
|
||||
cs.delayedTypeCheks = make(map[ast.Expr]delayedTypeValidator, 1)
|
||||
}
|
||||
cs.delayedTypeCheks[at] = check
|
||||
}
|
||||
|
||||
type typ struct {
|
||||
name string
|
||||
ir shaderir.Type
|
||||
|
@ -49,6 +49,10 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
for i := range ss {
|
||||
ss[i].IsTypeGuessed = true
|
||||
}
|
||||
|
||||
stmts = append(stmts, ss...)
|
||||
case token.ASSIGN:
|
||||
if len(stmt.Lhs) != len(stmt.Rhs) && len(stmt.Rhs) != 1 {
|
||||
@ -60,7 +64,7 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
return nil, false
|
||||
}
|
||||
stmts = append(stmts, ss...)
|
||||
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN, token.AND_NOT_ASSIGN:
|
||||
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN, token.AND_NOT_ASSIGN, token.SHL_ASSIGN, token.SHR_ASSIGN:
|
||||
rhs, rts, ss, ok := cs.parseExpr(block, fname, stmt.Rhs[0], true)
|
||||
if !ok {
|
||||
return nil, false
|
||||
@ -100,6 +104,10 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
op = shaderir.Or
|
||||
case token.XOR_ASSIGN:
|
||||
op = shaderir.Xor
|
||||
case token.SHL_ASSIGN:
|
||||
op = shaderir.LeftShift
|
||||
case token.SHR_ASSIGN:
|
||||
op = shaderir.RightShift
|
||||
default:
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("unexpected token: %s", stmt.Tok))
|
||||
return nil, false
|
||||
@ -110,7 +118,7 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator / not defined on %s", rts[0].String()))
|
||||
return nil, false
|
||||
}
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor {
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor || op == shaderir.LeftShift || op == shaderir.RightShift {
|
||||
if lts[0].Main != shaderir.Int && !lts[0].IsIntVector() {
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %s not defined on %s", stmt.Tok, lts[0].String()))
|
||||
}
|
||||
@ -137,7 +145,7 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
}
|
||||
}
|
||||
case shaderir.Float:
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor {
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor || op == shaderir.LeftShift || op == shaderir.RightShift {
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %s not defined on %s", stmt.Tok, lts[0].String()))
|
||||
} else if rhs[0].Const != nil &&
|
||||
(rts[0].Main == shaderir.None || rts[0].Main == shaderir.Float) &&
|
||||
@ -148,7 +156,7 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
return nil, false
|
||||
}
|
||||
case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor {
|
||||
if op == shaderir.And || op == shaderir.Or || op == shaderir.Xor || op == shaderir.LeftShift || op == shaderir.RightShift {
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("invalid operation: operator %s not defined on %s", stmt.Tok, lts[0].String()))
|
||||
} else if (op == shaderir.MatrixMul || op == shaderir.Div) &&
|
||||
(rts[0].Main == shaderir.Float ||
|
||||
@ -469,6 +477,25 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("unexpected statement: %#v", stmt))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Need to run delayed checks
|
||||
if len(cs.delayedTypeCheks) != 0 {
|
||||
for _, st := range stmts {
|
||||
cs.ValidateDefaultTypes(block, st)
|
||||
}
|
||||
|
||||
// Collect all errors first
|
||||
foundErr := false
|
||||
for s, v := range cs.delayedTypeCheks {
|
||||
if _, ok := v.IsValidated(); !ok {
|
||||
foundErr = true
|
||||
cs.addError(s.Pos(), v.Error())
|
||||
}
|
||||
}
|
||||
if foundErr {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return stmts, true
|
||||
}
|
||||
|
||||
|
@ -1314,6 +1314,263 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue: #2755
|
||||
func TestSyntaxOperatorShift(t *testing.T) {
|
||||
cases := []struct {
|
||||
stmt string
|
||||
err bool
|
||||
}{
|
||||
{stmt: "b := 2.0; a := 1.0 << 2.0 == b; _ = a", err: false},
|
||||
{stmt: "s := 1; b := 2.0; a := 1.0<<s == b; _ = a", err: true},
|
||||
{stmt: "s := 1; b := 2; a := 1.0<<s == b; _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0<<s + ivec2(3.0<<s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0<<s + vec2(3); _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2.0<<s + ivec2(3); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0<<s + foo_int_int(3.0<<s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0<<s + 3.0<<s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2<<s + 3.0<<s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2.0<<s + 3<<s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2<<s + 3<<s; _ = a", err: false},
|
||||
{stmt: "s := 1; foo_multivar(0, 0, 2<<s)", err: false},
|
||||
{stmt: "s := 1; foo_multivar(0, 2.0<<s, 0)", err: true},
|
||||
{stmt: "s := 1; foo_multivar(2.0<<s, 0, 0)", err: false},
|
||||
{stmt: "s := 1; a := foo_multivar(2.0<<s, 0, 0); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_multivar(0, 2.0<<s, 0); _ = a", err: true},
|
||||
{stmt: "s := 1; a := foo_multivar(0, 0, 2.0<<s); _ = a", err: false},
|
||||
{stmt: "a := foo_multivar(0, 0, 1.0<<2.0); _ = a", err: false},
|
||||
{stmt: "a := foo_multivar(0, 1.0<<2.0, 0); _ = a", err: false},
|
||||
{stmt: "s := 1; a := int(1) + 1.0<<s + int(float(1<<s)); _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = 1.0 << 2.0 << 3.0 << 4.0 << s; _ = a", err: false},
|
||||
{stmt: "s := 1; var a float = 1 << 1 << 1 << 1 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 << s + 1.2; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 << s + 1.2; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 << s + foo_float_float(2); _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 << s + foo_float_int(2); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_float_int(1.0<<s) + foo_float_int(2); _ = a", err: true},
|
||||
{stmt: "s := 1; a := foo_int_float(1<<s) + foo_int_float(2); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_int_int(1<<s) + foo_int_int(2); _ = a", err: false},
|
||||
{stmt: "s := 1; t := 2.0; a := t + 1.0 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; t := 2; a := t + 1.0 << s; _ = a", err: false},
|
||||
{stmt: "s := 1; b := 1 << s; _ = b", err: false},
|
||||
{stmt: "var a = 1; b := a << 2.0; _ = b", err: false},
|
||||
{stmt: "s := 1; var a float; a = 1 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = float(1.0 << s); _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = int(1 << s); _ = a", err: false},
|
||||
{stmt: "s := 1; var a int = int(1.0 << s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 1 << s; _ = a", err: false},
|
||||
{stmt: "s := 1; a := 1.0 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := int(1.0 << s); _ = a", err: false},
|
||||
{stmt: "s := 1; var a float = float(1.0 << s); _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1.0 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 << s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = 1.0 << s; _ = a", err: false},
|
||||
{stmt: "s := 1; var a int = 1 << s; _ = a", err: false},
|
||||
{stmt: "var a float = 1.0 << 2.0; _ = a", err: false},
|
||||
{stmt: "var a int = 1.0 << 2; _ = a", err: false},
|
||||
{stmt: "var a float = 1.0 << 2; _ = a", err: false},
|
||||
{stmt: "a := 1 << 2.0; _ = a", err: false},
|
||||
{stmt: "a := 1.0 << 2; _ = a", err: false},
|
||||
{stmt: "a := 1.0 << 2.0; _ = a", err: false},
|
||||
{stmt: "a := 1 << 2; _ = a", err: false},
|
||||
{stmt: "a := float(1.0) << 2; _ = a", err: true},
|
||||
{stmt: "a := 1 << float(2.0); _ = a", err: false},
|
||||
{stmt: "a := 1.0 << float(2.0); _ = a", err: false},
|
||||
{stmt: "a := ivec2(1) << 2; _ = a", err: false},
|
||||
{stmt: "a := 1 << ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) << float(2.0); _ = a", err: true},
|
||||
{stmt: "a := float(1.0) << ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) << ivec2(2); _ = a", err: false},
|
||||
{stmt: "a := ivec3(1) << ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) << ivec3(2); _ = a", err: true},
|
||||
{stmt: "a := 1 << vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) << 2; _ = a", err: true},
|
||||
{stmt: "a := float(1.0) << vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) << float(2.0); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) << vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) << vec3(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1) << vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) << ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) << vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1) << ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) << vec3(2); _ = a", err: true},
|
||||
|
||||
{stmt: "b := 2.0; a := 1.0 >> 2.0 == b; _ = a", err: false},
|
||||
{stmt: "s := 1; b := 2.0; a := 1.0>>s == b; _ = a", err: true},
|
||||
{stmt: "s := 1; b := 2; a := 1.0>>s == b; _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0>>s + ivec2(3.0>>s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0>>s + vec2(3); _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2.0>>s + ivec2(3); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0>>s + foo_int_int(3.0>>s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 2.0>>s + 3.0>>s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2>>s + 3.0>>s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2.0>>s + 3>>s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 2>>s + 3>>s; _ = a", err: false},
|
||||
{stmt: "s := 1; foo_multivar(0, 0, 2>>s)", err: false},
|
||||
{stmt: "s := 1; foo_multivar(0, 2.0>>s, 0)", err: true},
|
||||
{stmt: "s := 1; foo_multivar(2.0>>s, 0, 0)", err: false},
|
||||
{stmt: "s := 1; a := foo_multivar(2.0>>s, 0, 0); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_multivar(0, 2.0>>s, 0); _ = a", err: true},
|
||||
{stmt: "s := 1; a := foo_multivar(0, 0, 2.0>>s); _ = a", err: false},
|
||||
{stmt: "a := foo_multivar(0, 0, 1.0>>2.0); _ = a", err: false},
|
||||
{stmt: "a := foo_multivar(0, 1.0>>2.0, 0); _ = a", err: false},
|
||||
{stmt: "s := 1; a := int(1) + 1.0>>s + int(float(1>>s)); _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = 1.0 >> 2.0 >> 3.0 >> 4.0 >> s; _ = a", err: false},
|
||||
{stmt: "s := 1; var a float = 1 >> 1 >> 1 >> 1 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 >> s + 1.2; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 >> s + 1.2; _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 >> s + foo_float_float(2); _ = a", err: true},
|
||||
{stmt: "s := 1; a := 1.0 >> s + foo_float_int(2); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_float_int(1.0>>s) + foo_float_int(2); _ = a", err: true},
|
||||
{stmt: "s := 1; a := foo_int_float(1>>s) + foo_int_float(2); _ = a", err: false},
|
||||
{stmt: "s := 1; a := foo_int_int(1>>s) + foo_int_int(2); _ = a", err: false},
|
||||
{stmt: "s := 1; t := 2.0; a := t + 1.0 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; t := 2; a := t + 1.0 >> s; _ = a", err: false},
|
||||
{stmt: "s := 1; b := 1 >> s; _ = b", err: false},
|
||||
{stmt: "var a = 1; b := a >> 2.0; _ = b", err: false},
|
||||
{stmt: "s := 1; var a float; a = 1 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = float(1.0 >> s); _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = int(1 >> s); _ = a", err: false},
|
||||
{stmt: "s := 1; var a int = int(1.0 >> s); _ = a", err: false},
|
||||
{stmt: "s := 1; a := 1 >> s; _ = a", err: false},
|
||||
{stmt: "s := 1; a := 1.0 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; a := int(1.0 >> s); _ = a", err: false},
|
||||
{stmt: "s := 1; var a float = float(1.0 >> s); _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1.0 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a float = 1 >> s; _ = a", err: true},
|
||||
{stmt: "s := 1; var a int = 1.0 >> s; _ = a", err: false},
|
||||
{stmt: "s := 1; var a int = 1 >> s; _ = a", err: false},
|
||||
{stmt: "var a float = 1.0 >> 2.0; _ = a", err: false},
|
||||
{stmt: "var a int = 1.0 >> 2; _ = a", err: false},
|
||||
{stmt: "var a float = 1.0 >> 2; _ = a", err: false},
|
||||
{stmt: "a := 1 >> 2.0; _ = a", err: false},
|
||||
{stmt: "a := 1.0 >> 2; _ = a", err: false},
|
||||
{stmt: "a := 1.0 >> 2.0; _ = a", err: false},
|
||||
{stmt: "a := 1 >> 2; _ = a", err: false},
|
||||
{stmt: "a := float(1.0) >> 2; _ = a", err: true},
|
||||
{stmt: "a := 1 >> float(2.0); _ = a", err: false},
|
||||
{stmt: "a := 1.0 >> float(2.0); _ = a", err: false},
|
||||
{stmt: "a := ivec2(1) >> 2; _ = a", err: false},
|
||||
{stmt: "a := 1 >> ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) >> float(2.0); _ = a", err: true},
|
||||
{stmt: "a := float(1.0) >> ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) >> ivec2(2); _ = a", err: false},
|
||||
{stmt: "a := ivec3(1) >> ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) >> ivec3(2); _ = a", err: true},
|
||||
{stmt: "a := 1 >> vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) >> 2; _ = a", err: true},
|
||||
{stmt: "a := float(1.0) >> vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) >> float(2.0); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) >> vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) >> vec3(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1) >> vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1) >> ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) >> vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1) >> ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1) >> vec3(2); _ = a", err: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
_, err := compileToIR([]byte(fmt.Sprintf(`package main
|
||||
func foo_multivar(x int, y float, z int) int {return x}
|
||||
func foo_int_int(x int) int {return x}
|
||||
func foo_float_int(x float) int {return int(x)}
|
||||
func foo_int_float(x int) float {return float(x)}
|
||||
func foo_float_float(x float) float {return x}
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
%s
|
||||
return dstPos
|
||||
}`, c.stmt)))
|
||||
if err == nil && c.err {
|
||||
t.Errorf("%s must return an error but does not", c.stmt)
|
||||
} else if err != nil && !c.err {
|
||||
t.Errorf("%s must not return nil but returned %v", c.stmt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyntaxOperatorShiftAssign(t *testing.T) {
|
||||
cases := []struct {
|
||||
stmt string
|
||||
err bool
|
||||
}{
|
||||
{stmt: "a := 1; a <<= 2; _ = a", err: false},
|
||||
{stmt: "a := 1; a <<= 2.0; _ = a", err: false},
|
||||
{stmt: "a := float(1.0); a <<= 2; _ = a", err: true},
|
||||
{stmt: "a := 1; a <<= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= 2; _ = a", err: false},
|
||||
{stmt: "a := 1; a <<= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := float(1.0); a <<= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= ivec2(2); _ = a", err: false},
|
||||
{stmt: "a := ivec3(1); a <<= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= ivec3(2); _ = a", err: true},
|
||||
{stmt: "a := 1; a <<= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a <<= 2; _ = a", err: true},
|
||||
{stmt: "a := float(1.0); a <<= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a <<= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a <<= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a <<= vec3(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1); a <<= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a <<= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1); a <<= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a <<= vec3(2); _ = a", err: true},
|
||||
{stmt: "const c = 2; a := 1; a <<= c; _ = a", err: false},
|
||||
{stmt: "const c = 2.0; a := 1; a <<= c; _ = a", err: false},
|
||||
{stmt: "const c = 2; a := float(1.0); a <<= c; _ = a", err: true},
|
||||
{stmt: "const c float = 2; a := 1; a <<= c; _ = a", err: true},
|
||||
{stmt: "const c float = 2.0; a := 1; a <<= c; _ = a", err: true},
|
||||
{stmt: "const c int = 2; a := ivec2(1); a <<= c; _ = a", err: false},
|
||||
{stmt: "const c int = 2; a := vec2(1); a <<= c; _ = a", err: true},
|
||||
|
||||
{stmt: "a := 1; a >>= 2; _ = a", err: false},
|
||||
{stmt: "a := 1; a >>= 2.0; _ = a", err: false},
|
||||
{stmt: "a := float(1.0); a >>= 2; _ = a", err: true},
|
||||
{stmt: "a := 1; a >>= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= 2; _ = a", err: false},
|
||||
{stmt: "a := 1; a >>= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := float(1.0); a >>= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= ivec2(2); _ = a", err: false},
|
||||
{stmt: "a := ivec3(1); a >>= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= ivec3(2); _ = a", err: true},
|
||||
{stmt: "a := 1; a >>= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a >>= 2; _ = a", err: true},
|
||||
{stmt: "a := float(1.0); a >>= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a >>= float(2.0); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a >>= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a >>= vec3(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1); a >>= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec2(1); a >>= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= vec2(2); _ = a", err: true},
|
||||
{stmt: "a := vec3(1); a >>= ivec2(2); _ = a", err: true},
|
||||
{stmt: "a := ivec2(1); a >>= vec3(2); _ = a", err: true},
|
||||
{stmt: "const c = 2; a := 1; a >>= c; _ = a", err: false},
|
||||
{stmt: "const c = 2.0; a := 1; a >>= c; _ = a", err: false},
|
||||
{stmt: "const c = 2; a := float(1.0); a >>= c; _ = a", err: true},
|
||||
{stmt: "const c float = 2; a := 1; a >>= c; _ = a", err: true},
|
||||
{stmt: "const c float = 2.0; a := 1; a >>= c; _ = a", err: true},
|
||||
{stmt: "const c int = 2; a := ivec2(1); a >>= c; _ = a", err: false},
|
||||
{stmt: "const c int = 2; a := vec2(1); a >>= c; _ = a", err: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
_, err := compileToIR([]byte(fmt.Sprintf(`package main
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
%s
|
||||
return dstPos
|
||||
}`, c.stmt)))
|
||||
if err == nil && c.err {
|
||||
t.Errorf("%s must return an error but does not", c.stmt)
|
||||
} else if err != nil && !c.err {
|
||||
t.Errorf("%s must not return nil but returned %v", c.stmt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #1971
|
||||
func TestSyntaxOperatorMultiplyAssign(t *testing.T) {
|
||||
cases := []struct {
|
||||
|
@ -18,6 +18,29 @@ import (
|
||||
"go/constant"
|
||||
)
|
||||
|
||||
func ResolveUntypedConstsForBitShiftOp(lhs, rhs constant.Value, lhst, rhst Type) (newLhs, newRhs constant.Value, ok bool) {
|
||||
cLhs := lhs
|
||||
cRhs := rhs
|
||||
|
||||
// Right is const -> int
|
||||
if rhs != nil {
|
||||
cRhs = constant.ToInt(rhs)
|
||||
if cRhs.Kind() == constant.Unknown {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Left if untyped const -> int
|
||||
if lhs != nil && lhst.Main == None {
|
||||
cLhs = constant.ToInt(lhs)
|
||||
if cLhs.Kind() == constant.Unknown {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return cLhs, cRhs, true
|
||||
}
|
||||
|
||||
func ResolveUntypedConstsForBinaryOp(lhs, rhs constant.Value, lhst, rhst Type) (newLhs, newRhs constant.Value, ok bool) {
|
||||
if lhst.Main == None && rhst.Main == None {
|
||||
if lhs.Kind() == rhs.Kind() {
|
||||
@ -121,6 +144,16 @@ func TypeFromBinaryOp(op Op, lhst, rhst Type, lhsConst, rhsConst constant.Value)
|
||||
panic("shaderir: cannot resolve untyped values")
|
||||
}
|
||||
|
||||
if op == LeftShift || op == RightShift {
|
||||
if (lhst.Main == Int || lhst.IsIntVector()) && rhst.Main == Int {
|
||||
return lhst, true
|
||||
}
|
||||
if lhst.IsIntVector() && rhst.IsIntVector() && lhst.VectorElementCount() == rhst.VectorElementCount() {
|
||||
return lhst, true
|
||||
}
|
||||
return Type{}, false
|
||||
}
|
||||
|
||||
if op == AndAnd || op == OrOr {
|
||||
if lhst.Main == Bool && rhst.Main == Bool {
|
||||
return Type{Main: Bool}, true
|
||||
|
@ -16,6 +16,7 @@
|
||||
package shaderir
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"sort"
|
||||
@ -74,16 +75,17 @@ type Block struct {
|
||||
}
|
||||
|
||||
type Stmt struct {
|
||||
Type StmtType
|
||||
Exprs []Expr
|
||||
Blocks []*Block
|
||||
ForVarType Type
|
||||
ForVarIndex int
|
||||
ForInit constant.Value
|
||||
ForEnd constant.Value
|
||||
ForOp Op
|
||||
ForDelta constant.Value
|
||||
InitIndex int
|
||||
Type StmtType
|
||||
Exprs []Expr
|
||||
Blocks []*Block
|
||||
ForVarType Type
|
||||
ForVarIndex int
|
||||
ForInit constant.Value
|
||||
ForEnd constant.Value
|
||||
ForOp Op
|
||||
ForDelta constant.Value
|
||||
InitIndex int
|
||||
IsTypeGuessed bool
|
||||
}
|
||||
|
||||
type StmtType int
|
||||
@ -109,6 +111,7 @@ type Expr struct {
|
||||
Swizzling string
|
||||
Index int
|
||||
Op Op
|
||||
Ast ast.Expr
|
||||
}
|
||||
|
||||
type ExprType int
|
||||
|
@ -25,29 +25,29 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
var _ Face = (*StdFace)(nil)
|
||||
var _ Face = (*GoXFace)(nil)
|
||||
|
||||
type stdFaceGlyphImageCacheKey struct {
|
||||
type goXFaceGlyphImageCacheKey struct {
|
||||
rune rune
|
||||
xoffset fixed.Int26_6
|
||||
|
||||
// yoffset is always the same if the rune is the same, so this doesn't have to be a key.
|
||||
}
|
||||
|
||||
// StdFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font).
|
||||
// StdFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face.
|
||||
// StdFace must not be copied by value.
|
||||
type StdFace struct {
|
||||
// GoXFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font).
|
||||
// GoXFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face.
|
||||
// GoXFace must not be copied by value.
|
||||
type GoXFace struct {
|
||||
f *faceWithCache
|
||||
|
||||
glyphImageCache glyphImageCache[stdFaceGlyphImageCacheKey]
|
||||
glyphImageCache glyphImageCache[goXFaceGlyphImageCacheKey]
|
||||
|
||||
addr *StdFace
|
||||
addr *GoXFace
|
||||
}
|
||||
|
||||
// NewStdFace creates a new StdFace from a semi-standard font.Face.
|
||||
func NewStdFace(face font.Face) *StdFace {
|
||||
s := &StdFace{
|
||||
// NewGoXFace creates a new GoXFace from a semi-standard font.Face.
|
||||
func NewGoXFace(face font.Face) *GoXFace {
|
||||
s := &GoXFace{
|
||||
f: &faceWithCache{
|
||||
f: face,
|
||||
},
|
||||
@ -56,14 +56,14 @@ func NewStdFace(face font.Face) *StdFace {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StdFace) copyCheck() {
|
||||
func (s *GoXFace) copyCheck() {
|
||||
if s.addr != s {
|
||||
panic("text: illegal use of non-zero StdFace copied by value")
|
||||
panic("text: illegal use of non-zero GoXFace copied by value")
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics implements Face.
|
||||
func (s *StdFace) Metrics() Metrics {
|
||||
func (s *GoXFace) Metrics() Metrics {
|
||||
s.copyCheck()
|
||||
|
||||
m := s.f.Metrics()
|
||||
@ -77,24 +77,24 @@ func (s *StdFace) Metrics() Metrics {
|
||||
// UnsafeInternal returns its internal font.Face.
|
||||
//
|
||||
// This is unsafe since this might make internal cache states out of sync.
|
||||
func (s *StdFace) UnsafeInternal() font.Face {
|
||||
func (s *GoXFace) UnsafeInternal() font.Face {
|
||||
s.copyCheck()
|
||||
return s.f.f
|
||||
}
|
||||
|
||||
// advance implements Face.
|
||||
func (s *StdFace) advance(text string) float64 {
|
||||
func (s *GoXFace) advance(text string) float64 {
|
||||
return fixed26_6ToFloat64(font.MeasureString(s.f, text))
|
||||
}
|
||||
|
||||
// hasGlyph implements Face.
|
||||
func (s *StdFace) hasGlyph(r rune) bool {
|
||||
func (s *GoXFace) hasGlyph(r rune) bool {
|
||||
_, ok := s.f.GlyphAdvance(r)
|
||||
return ok
|
||||
}
|
||||
|
||||
// appendGlyphsForLine implements Face.
|
||||
func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||
func (s *GoXFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||
s.copyCheck()
|
||||
|
||||
origin := fixed.Point26_6{
|
||||
@ -129,8 +129,8 @@ func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset i
|
||||
return glyphs
|
||||
}
|
||||
|
||||
func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) {
|
||||
// Assume that StdFace's direction is always horizontal.
|
||||
func (s *GoXFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) {
|
||||
// Assume that GoXFace's direction is always horizontal.
|
||||
origin.X = adjustGranularity(origin.X, s)
|
||||
origin.Y &^= ((1 << 6) - 1)
|
||||
|
||||
@ -139,7 +139,7 @@ func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int
|
||||
X: (origin.X + b.Min.X) & ((1 << 6) - 1),
|
||||
Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1),
|
||||
}
|
||||
key := stdFaceGlyphImageCacheKey{
|
||||
key := goXFaceGlyphImageCacheKey{
|
||||
rune: r,
|
||||
xoffset: subpixelOffset.X,
|
||||
}
|
||||
@ -151,7 +151,7 @@ func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int
|
||||
return img, imgX, imgY, a
|
||||
}
|
||||
|
||||
func (s *StdFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image {
|
||||
func (s *GoXFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image {
|
||||
w, h := (glyphBounds.Max.X - glyphBounds.Min.X).Ceil(), (glyphBounds.Max.Y - glyphBounds.Min.Y).Ceil()
|
||||
if w == 0 || h == 0 {
|
||||
return nil
|
||||
@ -179,14 +179,14 @@ func (s *StdFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBo
|
||||
}
|
||||
|
||||
// direction implements Face.
|
||||
func (s *StdFace) direction() Direction {
|
||||
func (s *GoXFace) direction() Direction {
|
||||
return DirectionLeftToRight
|
||||
}
|
||||
|
||||
// appendVectorPathForLine implements Face.
|
||||
func (s *StdFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
|
||||
func (s *GoXFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
|
||||
}
|
||||
|
||||
// Metrics implements Face.
|
||||
func (s *StdFace) private() {
|
||||
func (s *GoXFace) private() {
|
||||
}
|
@ -89,7 +89,7 @@ type LayoutOptions struct {
|
||||
//
|
||||
// For horizontal directions, the start and end depends on the face.
|
||||
// If the face is GoTextFace, the start and the end depend on the Direction property.
|
||||
// If the face is StdFace, the start and the end are always left and right respectively.
|
||||
// If the face is GoXFace, the start and the end are always left and right respectively.
|
||||
//
|
||||
// For vertical directions, the start and end are top and bottom respectively.
|
||||
//
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
)
|
||||
|
||||
func TestMultiFace(t *testing.T) {
|
||||
faces := []text.Face{text.NewStdFace(bitmapfont.Face)}
|
||||
faces := []text.Face{text.NewGoXFace(bitmapfont.Face)}
|
||||
f, err := text.NewMultiFace(faces...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
)
|
||||
|
||||
// Face is an interface representing a font face.
|
||||
// The implementations are only faces defined in this package, like GoTextFace and StdFace.
|
||||
// The implementations are only faces defined in this package, like GoTextFace and GoXFace.
|
||||
type Face interface {
|
||||
// Metrics returns the metrics for this Face.
|
||||
Metrics() Metrics
|
||||
@ -59,15 +59,15 @@ type Metrics struct {
|
||||
HDescent float64
|
||||
|
||||
// VLineGap is the recommended amount of horizontal space between two lines of text in pixels.
|
||||
// If the face is StdFace or the font doesn't support a vertical direction, VLineGap is 0.
|
||||
// If the face is GoXFace or the font doesn't support a vertical direction, VLineGap is 0.
|
||||
VLineGap float64
|
||||
|
||||
// VAscent is the distance in pixels from the top of a line to its baseline for vertical lines.
|
||||
// If the face is StdFace or the font doesn't support a vertical direction, VAscent is 0.
|
||||
// If the face is GoXFace or the font doesn't support a vertical direction, VAscent is 0.
|
||||
VAscent float64
|
||||
|
||||
// VDescent is the distance in pixels from the top of a line to its baseline for vertical lines.
|
||||
// If the face is StdFace or the font doesn't support a vertical direction, VDescent is 0.
|
||||
// If the face is GoXFace or the font doesn't support a vertical direction, VDescent is 0.
|
||||
VDescent float64
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ func TestGlyphIndex(t *testing.T) {
|
||||
const sampleText = `The quick brown fox jumps
|
||||
over the lazy dog.`
|
||||
|
||||
f := text.NewStdFace(bitmapfont.Face)
|
||||
f := text.NewGoXFace(bitmapfont.Face)
|
||||
got := sampleText
|
||||
for _, g := range text.AppendGlyphs(nil, sampleText, f, nil) {
|
||||
got = got[:g.StartIndexInBytes] + strings.Repeat(" ", g.EndIndexInBytes-g.StartIndexInBytes) + got[g.EndIndexInBytes:]
|
||||
@ -55,7 +55,7 @@ func TestTextColor(t *testing.T) {
|
||||
op := &text.DrawOptions{}
|
||||
op.GeoM.Translate(0, 0)
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
text.Draw(img, "Hello", text.NewStdFace(bitmapfont.Face), op)
|
||||
text.Draw(img, "Hello", text.NewGoXFace(bitmapfont.Face), op)
|
||||
|
||||
w, h := img.Bounds().Dx(), img.Bounds().Dy()
|
||||
allTransparent := true
|
||||
@ -77,12 +77,12 @@ func TestTextColor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const testStdFaceSize = 6
|
||||
const testGoXFaceSize = 6
|
||||
|
||||
type testStdFace struct{}
|
||||
type testGoXFace struct{}
|
||||
|
||||
func (f *testStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
dr = image.Rect(0, 0, testStdFaceSize, testStdFaceSize)
|
||||
func (f *testGoXFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
dr = image.Rect(0, 0, testGoXFaceSize, testGoXFaceSize)
|
||||
a := image.NewAlpha(dr)
|
||||
switch r {
|
||||
case 'a':
|
||||
@ -99,56 +99,56 @@ func (f *testStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, ma
|
||||
}
|
||||
}
|
||||
mask = a
|
||||
advance = fixed.I(testStdFaceSize)
|
||||
advance = fixed.I(testGoXFaceSize)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (f *testStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
bounds = fixed.R(0, 0, testStdFaceSize, testStdFaceSize)
|
||||
advance = fixed.I(testStdFaceSize)
|
||||
func (f *testGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
bounds = fixed.R(0, 0, testGoXFaceSize, testGoXFaceSize)
|
||||
advance = fixed.I(testGoXFaceSize)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (f *testStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
return fixed.I(testStdFaceSize), true
|
||||
func (f *testGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
return fixed.I(testGoXFaceSize), true
|
||||
}
|
||||
|
||||
func (f *testStdFace) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||
func (f *testGoXFace) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||
if r1 == 'b' {
|
||||
return fixed.I(-testStdFaceSize)
|
||||
return fixed.I(-testGoXFaceSize)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *testStdFace) Close() error {
|
||||
func (f *testGoXFace) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *testStdFace) Metrics() font.Metrics {
|
||||
func (f *testGoXFace) Metrics() font.Metrics {
|
||||
return font.Metrics{
|
||||
Height: fixed.I(testStdFaceSize),
|
||||
Height: fixed.I(testGoXFaceSize),
|
||||
Ascent: 0,
|
||||
Descent: fixed.I(testStdFaceSize),
|
||||
Descent: fixed.I(testGoXFaceSize),
|
||||
XHeight: 0,
|
||||
CapHeight: fixed.I(testStdFaceSize),
|
||||
CapHeight: fixed.I(testGoXFaceSize),
|
||||
CaretSlope: image.Pt(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #1378
|
||||
func TestNegativeKern(t *testing.T) {
|
||||
f := text.NewStdFace(&testStdFace{})
|
||||
dst := ebiten.NewImage(testStdFaceSize*2, testStdFaceSize)
|
||||
f := text.NewGoXFace(&testGoXFace{})
|
||||
dst := ebiten.NewImage(testGoXFaceSize*2, testGoXFaceSize)
|
||||
|
||||
// With testStdFace, 'b' is rendered at the previous position as 0xff.
|
||||
// With testGoXFace, 'b' is rendered at the previous position as 0xff.
|
||||
// 'a' is rendered at the current position as 0x80.
|
||||
op := &text.DrawOptions{}
|
||||
op.GeoM.Translate(0, 0)
|
||||
text.Draw(dst, "ab", f, op)
|
||||
for j := 0; j < testStdFaceSize; j++ {
|
||||
for i := 0; i < testStdFaceSize; i++ {
|
||||
for j := 0; j < testGoXFaceSize; j++ {
|
||||
for i := 0; i < testGoXFaceSize; i++ {
|
||||
got := dst.At(i, j)
|
||||
want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
||||
if got != want {
|
||||
@ -159,10 +159,10 @@ func TestNegativeKern(t *testing.T) {
|
||||
|
||||
// The glyph 'a' should be treated correctly.
|
||||
op = &text.DrawOptions{}
|
||||
op.GeoM.Translate(testStdFaceSize, 0)
|
||||
op.GeoM.Translate(testGoXFaceSize, 0)
|
||||
text.Draw(dst, "a", f, op)
|
||||
for j := 0; j < testStdFaceSize; j++ {
|
||||
for i := testStdFaceSize; i < testStdFaceSize*2; i++ {
|
||||
for j := 0; j < testGoXFaceSize; j++ {
|
||||
for i := testGoXFaceSize; i < testGoXFaceSize*2; i++ {
|
||||
got := dst.At(i, j)
|
||||
want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}
|
||||
if got != want {
|
||||
@ -172,12 +172,12 @@ func TestNegativeKern(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type unhashableStdFace func()
|
||||
type unhashableGoXFace func()
|
||||
|
||||
const unhashableStdFaceSize = 10
|
||||
const unhashableGoXFaceSize = 10
|
||||
|
||||
func (u *unhashableStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
dr = image.Rect(0, 0, unhashableStdFaceSize, unhashableStdFaceSize)
|
||||
func (u *unhashableGoXFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
dr = image.Rect(0, 0, unhashableGoXFaceSize, unhashableGoXFaceSize)
|
||||
a := image.NewAlpha(dr)
|
||||
for j := dr.Min.Y; j < dr.Max.Y; j++ {
|
||||
for i := dr.Min.X; i < dr.Max.X; i++ {
|
||||
@ -185,53 +185,53 @@ func (u *unhashableStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectang
|
||||
}
|
||||
}
|
||||
mask = a
|
||||
advance = fixed.I(unhashableStdFaceSize)
|
||||
advance = fixed.I(unhashableGoXFaceSize)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (u *unhashableStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
bounds = fixed.R(0, 0, unhashableStdFaceSize, unhashableStdFaceSize)
|
||||
advance = fixed.I(unhashableStdFaceSize)
|
||||
func (u *unhashableGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
bounds = fixed.R(0, 0, unhashableGoXFaceSize, unhashableGoXFaceSize)
|
||||
advance = fixed.I(unhashableGoXFaceSize)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (u *unhashableStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
return fixed.I(unhashableStdFaceSize), true
|
||||
func (u *unhashableGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
return fixed.I(unhashableGoXFaceSize), true
|
||||
}
|
||||
|
||||
func (u *unhashableStdFace) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||
func (u *unhashableGoXFace) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (u *unhashableStdFace) Close() error {
|
||||
func (u *unhashableGoXFace) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *unhashableStdFace) Metrics() font.Metrics {
|
||||
func (u *unhashableGoXFace) Metrics() font.Metrics {
|
||||
return font.Metrics{
|
||||
Height: fixed.I(unhashableStdFaceSize),
|
||||
Height: fixed.I(unhashableGoXFaceSize),
|
||||
Ascent: 0,
|
||||
Descent: fixed.I(unhashableStdFaceSize),
|
||||
Descent: fixed.I(unhashableGoXFaceSize),
|
||||
XHeight: 0,
|
||||
CapHeight: fixed.I(unhashableStdFaceSize),
|
||||
CapHeight: fixed.I(unhashableGoXFaceSize),
|
||||
CaretSlope: image.Pt(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #2669
|
||||
func TestUnhashableFace(t *testing.T) {
|
||||
var face unhashableStdFace
|
||||
f := text.NewStdFace(&face)
|
||||
dst := ebiten.NewImage(unhashableStdFaceSize*2, unhashableStdFaceSize*2)
|
||||
var face unhashableGoXFace
|
||||
f := text.NewGoXFace(&face)
|
||||
dst := ebiten.NewImage(unhashableGoXFaceSize*2, unhashableGoXFaceSize*2)
|
||||
text.Draw(dst, "a", f, nil)
|
||||
|
||||
for j := 0; j < unhashableStdFaceSize*2; j++ {
|
||||
for i := 0; i < unhashableStdFaceSize*2; i++ {
|
||||
for j := 0; j < unhashableGoXFaceSize*2; j++ {
|
||||
for i := 0; i < unhashableGoXFaceSize*2; i++ {
|
||||
got := dst.At(i, j)
|
||||
var want color.RGBA
|
||||
if i < unhashableStdFaceSize && j < unhashableStdFaceSize {
|
||||
if i < unhashableGoXFaceSize && j < unhashableGoXFaceSize {
|
||||
want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
||||
}
|
||||
if got != want {
|
||||
|
Loading…
Reference in New Issue
Block a user