mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-09-26 20:11:28 +08:00
@@ -102,7 +102,7 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error)
|
|||||||
return vsh, psh, nil
|
return vsh, psh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
vs, ps, _ := hlsl.Compile(program)
|
vs, ps, _, _ := hlsl.Compile(program)
|
||||||
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
||||||
|
|
||||||
var wg errgroup.Group
|
var wg errgroup.Group
|
||||||
|
@@ -421,6 +421,16 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
|||||||
return nil, nil, nil, false
|
return nil, nil, nil, false
|
||||||
}
|
}
|
||||||
finalType = shaderir.Type{Main: shaderir.Vec4}
|
finalType = shaderir.Type{Main: shaderir.Vec4}
|
||||||
|
case shaderir.FrontFacing:
|
||||||
|
if len(args) != 0 {
|
||||||
|
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 0 but %d", callee.BuiltinFunc, len(args)))
|
||||||
|
return nil, nil, nil, false
|
||||||
|
}
|
||||||
|
if fname != cs.fragmentEntry {
|
||||||
|
cs.addError(e.Pos(), fmt.Sprintf("frontfacing is available only in %s", cs.fragmentEntry))
|
||||||
|
return nil, nil, nil, false
|
||||||
|
}
|
||||||
|
finalType = shaderir.Type{Main: shaderir.Bool}
|
||||||
case shaderir.DiscardF:
|
case shaderir.DiscardF:
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 0 but %d", callee.BuiltinFunc, len(args)))
|
cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 0 but %d", callee.BuiltinFunc, len(args)))
|
||||||
@@ -434,7 +444,6 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
|||||||
Type: shaderir.Discard,
|
Type: shaderir.Discard,
|
||||||
})
|
})
|
||||||
return nil, nil, stmts, true
|
return nil, nil, stmts, true
|
||||||
|
|
||||||
case shaderir.Clamp, shaderir.Mix, shaderir.Smoothstep, shaderir.Faceforward, shaderir.Refract:
|
case shaderir.Clamp, shaderir.Mix, shaderir.Smoothstep, shaderir.Faceforward, shaderir.Refract:
|
||||||
// 3 arguments
|
// 3 arguments
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
|
@@ -180,8 +180,8 @@ func TestCompile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tc.HLSL != nil {
|
if tc.HLSL != nil {
|
||||||
vs, _, prelude := hlsl.Compile(s)
|
vs, _, vertexPrelude, _ := hlsl.Compile(s)
|
||||||
if got, want := hlslNormalize(vs, prelude), hlslNormalize(string(tc.HLSL), prelude); got != want {
|
if got, want := hlslNormalize(vs, vertexPrelude), hlslNormalize(string(tc.HLSL), vertexPrelude); got != want {
|
||||||
compare(t, "HLSL", got, want)
|
compare(t, "HLSL", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
void F0(thread array<float2, 3>& l0);
|
void F0(bool front_facing, thread array<float2, 3>& l0);
|
||||||
|
|
||||||
void F0(thread array<float2, 3>& l0) {
|
void F0(bool front_facing, thread array<float2, 3>& l0) {
|
||||||
array<float2, 2> l1 = {};
|
array<float2, 2> l1 = {};
|
||||||
array<float2, 3> l2 = {};
|
array<float2, 3> l2 = {};
|
||||||
{
|
{
|
||||||
|
4
internal/shader/testdata/atan.expected.metal
vendored
4
internal/shader/testdata/atan.expected.metal
vendored
@@ -1,6 +1,6 @@
|
|||||||
bool F0(float l0, float l1);
|
bool F0(bool front_facing, float l0, float l1);
|
||||||
|
|
||||||
bool F0(float l0, float l1) {
|
bool F0(bool front_facing, float l0, float l1) {
|
||||||
float l2 = float(0);
|
float l2 = float(0);
|
||||||
float l3 = float(0);
|
float l3 = float(0);
|
||||||
l2 = atan((l1) / (l0));
|
l2 = atan((l1) / (l0));
|
||||||
|
4
internal/shader/testdata/out.expected.metal
vendored
4
internal/shader/testdata/out.expected.metal
vendored
@@ -1,6 +1,6 @@
|
|||||||
void F0(thread float& l0, thread array<float, 4>& l1, thread float4& l2);
|
void F0(bool front_facing, thread float& l0, thread array<float, 4>& l1, thread float4& l2);
|
||||||
|
|
||||||
void F0(thread float& l0, thread array<float, 4>& l1, thread float4& l2) {
|
void F0(bool front_facing, thread float& l0, thread array<float, 4>& l1, thread float4& l2) {
|
||||||
l0 = float(0);
|
l0 = float(0);
|
||||||
l1 = {};
|
l1 = {};
|
||||||
l2 = float4(0);
|
l2 = float4(0);
|
||||||
|
@@ -29,6 +29,7 @@ vertex Varyings Vertex(
|
|||||||
|
|
||||||
fragment float4 Fragment(
|
fragment float4 Fragment(
|
||||||
Varyings varyings [[stage_in]],
|
Varyings varyings [[stage_in]],
|
||||||
constant Uniforms& uniforms [[buffer(0)]]) {
|
constant Uniforms& uniforms [[buffer(0)]],
|
||||||
|
bool front_facing [[front_facing]]) {
|
||||||
return float4((varyings.Position).x, (varyings.M0).y, (varyings.M1).z, 1.0);
|
return float4((varyings.Position).x, (varyings.M0).y, (varyings.M1).z, 1.0);
|
||||||
}
|
}
|
||||||
|
@@ -520,6 +520,9 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
if callee.BuiltinFunc == shaderir.FrontFacing {
|
||||||
|
return "gl_FrontFacing"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Using parentheses at the callee is illegal.
|
// Using parentheses at the callee is illegal.
|
||||||
return fmt.Sprintf("%s(%s)", expr(&callee), strings.Join(args, ", "))
|
return fmt.Sprintf("%s(%s)", expr(&callee), strings.Join(args, ", "))
|
||||||
|
@@ -80,73 +80,84 @@ float4x4 float4x4FromScalar(float x) {
|
|||||||
return float4x4(x, 0, 0, 0, 0, x, 0, 0, 0, 0, x, 0, 0, 0, 0, x);
|
return float4x4(x, 0, 0, 0, 0, x, 0, 0, 0, 0, x, 0, 0, 0, 0, x);
|
||||||
}`
|
}`
|
||||||
|
|
||||||
func Compile(p *shaderir.Program) (vertexShader, pixelShader, prelude string) {
|
func Compile(p *shaderir.Program) (vertexShader, pixelShader, vertexPrelude, pixelPrelude string) {
|
||||||
offsets := UniformVariableOffsetsInDwords(p)
|
offsets := UniformVariableOffsetsInDwords(p)
|
||||||
|
|
||||||
c := &compileContext{
|
c := &compileContext{
|
||||||
unit: p.Unit,
|
unit: p.Unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines []string
|
appendPrelude := func(lines []string, vertex bool) []string {
|
||||||
lines = append(lines, strings.Split(utilFuncs, "\n")...)
|
lines = append(lines, strings.Split(utilFuncs, "\n")...)
|
||||||
|
|
||||||
lines = append(lines, "", "struct Varyings {")
|
lines = append(lines, "", "struct Varyings {")
|
||||||
lines = append(lines, "\tfloat4 Position : SV_POSITION;")
|
lines = append(lines, "\tfloat4 Position : SV_POSITION;")
|
||||||
if len(p.Varyings) > 0 {
|
if len(p.Varyings) > 0 {
|
||||||
for i, v := range p.Varyings {
|
for i, v := range p.Varyings {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
lines = append(lines, fmt.Sprintf("\tfloat2 M%d : TEXCOORD;", i))
|
lines = append(lines, fmt.Sprintf("\tfloat2 M%d : TEXCOORD;", i))
|
||||||
case 1:
|
case 1:
|
||||||
lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR0;", i))
|
lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR0;", i))
|
||||||
default:
|
default:
|
||||||
// Use COLOR[n] as a general purpose varying.
|
// Use COLOR[n] as a general purpose varying.
|
||||||
if v.Main != shaderir.Vec4 {
|
if v.Main != shaderir.Vec4 {
|
||||||
lines = append(lines, fmt.Sprintf("\t?(unexpected type: %s) M%d : COLOR%d;", v, i, i-1))
|
lines = append(lines, fmt.Sprintf("\t?(unexpected type: %s) M%d : COLOR%d;", v, i, i-1))
|
||||||
} else {
|
} else {
|
||||||
lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR%d;", i, i-1))
|
lines = append(lines, fmt.Sprintf("\tfloat4 M%d : COLOR%d;", i, i-1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !vertex {
|
||||||
|
lines = append(lines, "\tbool FrontFacing : SV_IsFrontFace;")
|
||||||
|
}
|
||||||
|
lines = append(lines, "};")
|
||||||
|
return lines
|
||||||
}
|
}
|
||||||
lines = append(lines, "};")
|
|
||||||
prelude = strings.Join(lines, "\n")
|
|
||||||
|
|
||||||
lines = append(lines, "", "{{.Structs}}")
|
var vslines, pslines []string
|
||||||
|
vslines = appendPrelude(vslines, true)
|
||||||
|
pslines = appendPrelude(pslines, false)
|
||||||
|
|
||||||
if len(p.Uniforms) > 0 {
|
vertexPrelude = strings.Join(vslines, "\n")
|
||||||
lines = append(lines, "")
|
pixelPrelude = strings.Join(pslines, "\n")
|
||||||
lines = append(lines, "cbuffer Uniforms : register(b0) {")
|
|
||||||
for i, t := range p.Uniforms {
|
appendGlobalVariables := func(lines []string) []string {
|
||||||
// packingoffset is not mandatory, but this is useful to ensure the correct offset is used.
|
lines = append(lines, "", "{{.Structs}}")
|
||||||
offset := fmt.Sprintf("c%d", offsets[i]/UniformVariableBoundaryInDwords)
|
|
||||||
switch offsets[i] % UniformVariableBoundaryInDwords {
|
if len(p.Uniforms) > 0 {
|
||||||
case 1:
|
lines = append(lines, "")
|
||||||
offset += ".y"
|
lines = append(lines, "cbuffer Uniforms : register(b0) {")
|
||||||
case 2:
|
for i, t := range p.Uniforms {
|
||||||
offset += ".z"
|
// packingoffset is not mandatory, but this is useful to ensure the correct offset is used.
|
||||||
case 3:
|
offset := fmt.Sprintf("c%d", offsets[i]/UniformVariableBoundaryInDwords)
|
||||||
offset += ".w"
|
switch offsets[i] % UniformVariableBoundaryInDwords {
|
||||||
|
case 1:
|
||||||
|
offset += ".y"
|
||||||
|
case 2:
|
||||||
|
offset += ".z"
|
||||||
|
case 3:
|
||||||
|
offset += ".w"
|
||||||
|
}
|
||||||
|
lines = append(lines, fmt.Sprintf("\t%s : packoffset(%s);", c.varDecl(p, &t, fmt.Sprintf("U%d", i)), offset))
|
||||||
}
|
}
|
||||||
lines = append(lines, fmt.Sprintf("\t%s : packoffset(%s);", c.varDecl(p, &t, fmt.Sprintf("U%d", i)), offset))
|
lines = append(lines, "}")
|
||||||
}
|
}
|
||||||
lines = append(lines, "}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.TextureCount > 0 {
|
if p.TextureCount > 0 {
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
for i := 0; i < p.TextureCount; i++ {
|
for i := 0; i < p.TextureCount; i++ {
|
||||||
lines = append(lines, fmt.Sprintf("Texture2D T%[1]d : register(t%[1]d);", i))
|
lines = append(lines, fmt.Sprintf("Texture2D T%[1]d : register(t%[1]d);", i))
|
||||||
}
|
}
|
||||||
if c.unit == shaderir.Texels {
|
if c.unit == shaderir.Texels {
|
||||||
lines = append(lines, "SamplerState samp : register(s0);")
|
lines = append(lines, "SamplerState samp : register(s0);")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return lines
|
||||||
}
|
}
|
||||||
|
vslines = appendGlobalVariables(vslines)
|
||||||
vslines := make([]string, len(lines))
|
pslines = appendGlobalVariables(pslines)
|
||||||
copy(vslines, lines)
|
|
||||||
pslines := make([]string, len(lines))
|
|
||||||
copy(pslines, lines)
|
|
||||||
|
|
||||||
var vsfuncs []*shaderir.Func
|
var vsfuncs []*shaderir.Func
|
||||||
if p.VertexFunc.Block != nil {
|
if p.VertexFunc.Block != nil {
|
||||||
@@ -522,6 +533,9 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
if callee.Type == shaderir.BuiltinFuncExpr && callee.BuiltinFunc == shaderir.FrontFacing {
|
||||||
|
return fmt.Sprintf("%s.FrontFacing", vsOut)
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s(%s)", expr(&e.Exprs[0]), strings.Join(args, ", "))
|
return fmt.Sprintf("%s(%s)", expr(&e.Exprs[0]), strings.Join(args, ", "))
|
||||||
case shaderir.FieldSelector:
|
case shaderir.FieldSelector:
|
||||||
return fmt.Sprintf("(%s).%s", expr(&e.Exprs[0]), expr(&e.Exprs[1]))
|
return fmt.Sprintf("(%s).%s", expr(&e.Exprs[0]), expr(&e.Exprs[1]))
|
||||||
|
@@ -819,9 +819,9 @@ void F0(in float l0, in float l1, out float l2) {
|
|||||||
}`,
|
}`,
|
||||||
Metal: msl.Prelude(shaderir.Pixels) + `
|
Metal: msl.Prelude(shaderir.Pixels) + `
|
||||||
|
|
||||||
void F0(float l0, float l1, thread float& l2);
|
void F0(bool front_facing, float l0, float l1, thread float& l2);
|
||||||
|
|
||||||
void F0(float l0, float l1, thread float& l2) {
|
void F0(bool front_facing, float l0, float l1, thread float& l2) {
|
||||||
for (int l3 = 0; l3 < 100; l3++) {
|
for (int l3 = 0; l3 < 100; l3++) {
|
||||||
int l4 = 0;
|
int l4 = 0;
|
||||||
l2 = l4;
|
l2 = l4;
|
||||||
@@ -916,9 +916,9 @@ void F0(in float l0, in float l1, out float l2) {
|
|||||||
}`,
|
}`,
|
||||||
Metal: msl.Prelude(shaderir.Pixels) + `
|
Metal: msl.Prelude(shaderir.Pixels) + `
|
||||||
|
|
||||||
void F0(float l0, float l1, thread float& l2);
|
void F0(bool front_facing, float l0, float l1, thread float& l2);
|
||||||
|
|
||||||
void F0(float l0, float l1, thread float& l2) {
|
void F0(bool front_facing, float l0, float l1, thread float& l2) {
|
||||||
for (int l3 = 0; l3 < 100; l3++) {
|
for (int l3 = 0; l3 < 100; l3++) {
|
||||||
int l4 = 0;
|
int l4 = 0;
|
||||||
l2 = l4;
|
l2 = l4;
|
||||||
|
@@ -156,6 +156,8 @@ func Compile(p *shaderir.Program) (shader string) {
|
|||||||
lines[len(lines)-1] += ","
|
lines[len(lines)-1] += ","
|
||||||
lines = append(lines, fmt.Sprintf("\ttexture2d<float> T%[1]d [[texture(%[1]d)]]", i))
|
lines = append(lines, fmt.Sprintf("\ttexture2d<float> T%[1]d [[texture(%[1]d)]]", i))
|
||||||
}
|
}
|
||||||
|
lines[len(lines)-1] += ","
|
||||||
|
lines = append(lines, "\tbool front_facing [[front_facing]]")
|
||||||
lines[len(lines)-1] += ") {"
|
lines[len(lines)-1] += ") {"
|
||||||
lines = append(lines, c.block(p, p.FragmentFunc.Block, p.FragmentFunc.Block, 0)...)
|
lines = append(lines, c.block(p, p.FragmentFunc.Block, p.FragmentFunc.Block, 0)...)
|
||||||
lines = append(lines, "}")
|
lines = append(lines, "}")
|
||||||
@@ -244,6 +246,7 @@ func (c *compileContext) function(p *shaderir.Program, f *shaderir.Func, prototy
|
|||||||
for i := 0; i < p.TextureCount; i++ {
|
for i := 0; i < p.TextureCount; i++ {
|
||||||
args = append(args, fmt.Sprintf("texture2d<float> T%d", i))
|
args = append(args, fmt.Sprintf("texture2d<float> T%d", i))
|
||||||
}
|
}
|
||||||
|
args = append(args, "bool front_facing")
|
||||||
|
|
||||||
var idx int
|
var idx int
|
||||||
for _, t := range f.InParams {
|
for _, t := range f.InParams {
|
||||||
@@ -404,6 +407,7 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
|||||||
for i := 0; i < p.TextureCount; i++ {
|
for i := 0; i < p.TextureCount; i++ {
|
||||||
args = append(args, fmt.Sprintf("T%d", i))
|
args = append(args, fmt.Sprintf("T%d", i))
|
||||||
}
|
}
|
||||||
|
args = append(args, "front_facing")
|
||||||
}
|
}
|
||||||
for _, exp := range e.Exprs[1:] {
|
for _, exp := range e.Exprs[1:] {
|
||||||
args = append(args, expr(&exp))
|
args = append(args, expr(&exp))
|
||||||
@@ -425,6 +429,9 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
if callee.Type == shaderir.BuiltinFuncExpr && callee.BuiltinFunc == shaderir.FrontFacing {
|
||||||
|
return "(front_facing)"
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s(%s)", expr(&callee), strings.Join(args, ", "))
|
return fmt.Sprintf("%s(%s)", expr(&callee), strings.Join(args, ", "))
|
||||||
case shaderir.FieldSelector:
|
case shaderir.FieldSelector:
|
||||||
return fmt.Sprintf("(%s).%s", expr(&e.Exprs[0]), expr(&e.Exprs[1]))
|
return fmt.Sprintf("(%s).%s", expr(&e.Exprs[0]), expr(&e.Exprs[1]))
|
||||||
|
@@ -294,6 +294,7 @@ const (
|
|||||||
Fwidth BuiltinFunc = "fwidth"
|
Fwidth BuiltinFunc = "fwidth"
|
||||||
DiscardF BuiltinFunc = "discard"
|
DiscardF BuiltinFunc = "discard"
|
||||||
TexelAt BuiltinFunc = "__texelAt"
|
TexelAt BuiltinFunc = "__texelAt"
|
||||||
|
FrontFacing BuiltinFunc = "frontfacing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
|
func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
|
||||||
@@ -351,7 +352,8 @@ func ParseBuiltinFunc(str string) (BuiltinFunc, bool) {
|
|||||||
Dfdy,
|
Dfdy,
|
||||||
Fwidth,
|
Fwidth,
|
||||||
DiscardF,
|
DiscardF,
|
||||||
TexelAt:
|
TexelAt,
|
||||||
|
FrontFacing:
|
||||||
return BuiltinFunc(str), true
|
return BuiltinFunc(str), true
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
|
@@ -497,7 +497,7 @@ func compile(shader *Shader, targets []string) error {
|
|||||||
Fragment: fs,
|
Fragment: fs,
|
||||||
}
|
}
|
||||||
case "hlsl":
|
case "hlsl":
|
||||||
vs, ps, _ := hlsl.Compile(ir)
|
vs, ps, _, _ := hlsl.Compile(ir)
|
||||||
shader.HLSL = &HLSL{
|
shader.HLSL = &HLSL{
|
||||||
Vertex: vs,
|
Vertex: vs,
|
||||||
Pixel: ps,
|
Pixel: ps,
|
||||||
|
@@ -3010,3 +3010,56 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShaderFrontFacing(t *testing.T) {
|
||||||
|
const w, h = 16, 16
|
||||||
|
|
||||||
|
dst := ebiten.NewImage(w, h)
|
||||||
|
s, err := ebiten.NewShader([]byte(`//kage:unit pixels
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||||
|
if frontfacing() {
|
||||||
|
return vec4(0.5, 0, 0, 1)
|
||||||
|
}
|
||||||
|
return vec4(0, 0.5, 0, 1)
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := []ebiten.Vertex{
|
||||||
|
{
|
||||||
|
DstX: 0,
|
||||||
|
DstY: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: w,
|
||||||
|
DstY: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: 0,
|
||||||
|
DstY: h,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DstX: w,
|
||||||
|
DstY: h,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
op := &ebiten.DrawTrianglesShaderOptions{}
|
||||||
|
op.Blend = ebiten.BlendLighter
|
||||||
|
dst.DrawTrianglesShader32(vs, []uint32{0, 1, 2, 1, 2, 3}, s, op)
|
||||||
|
dst.DrawTrianglesShader32(vs, []uint32{2, 1, 0, 3, 2, 1}, s, op)
|
||||||
|
|
||||||
|
for j := 0; j < h; j++ {
|
||||||
|
for i := 0; i < w; i++ {
|
||||||
|
got := dst.At(i, j).(color.RGBA)
|
||||||
|
want := color.RGBA{R: 0x80, G: 0x80, B: 0x00, A: 0xff}
|
||||||
|
if !sameColors(got, want, 2) {
|
||||||
|
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user