mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
parent
8c5f525ac2
commit
156c34a316
80
colorm/colorm.go
Normal file
80
colorm/colorm.go
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2022 The Ebitengine 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 colorm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
|
||||
)
|
||||
|
||||
// Dim is a dimension of a ColorM.
|
||||
const Dim = ebiten.ColorMDim
|
||||
|
||||
// ColorM represents a matrix to transform coloring when rendering an image.
|
||||
//
|
||||
// ColorM is applied to the straight alpha color
|
||||
// while an Image's pixels' format is alpha premultiplied.
|
||||
// Before applying a matrix, a color is un-multiplied, and after applying the matrix,
|
||||
// the color is multiplied again.
|
||||
//
|
||||
// The initial value is identity.
|
||||
type ColorM = ebiten.ColorM
|
||||
|
||||
func uniforms(c ColorM) map[string]interface{} {
|
||||
var body [16]float32
|
||||
var translation [4]float32
|
||||
c.ReadElements(body[:], translation[:])
|
||||
|
||||
uniforms := map[string]interface{}{}
|
||||
uniforms[builtinshader.UniformColorMBody] = body[:]
|
||||
uniforms[builtinshader.UniformColorMTranslation] = translation[:]
|
||||
return uniforms
|
||||
}
|
||||
|
||||
type builtinShaderKey struct {
|
||||
filter builtinshader.Filter
|
||||
address builtinshader.Address
|
||||
}
|
||||
|
||||
var (
|
||||
builtinShaders = map[builtinShaderKey]*ebiten.Shader{}
|
||||
builtinShadersM sync.Mutex
|
||||
)
|
||||
|
||||
func builtinShader(filter builtinshader.Filter, address builtinshader.Address) *ebiten.Shader {
|
||||
builtinShadersM.Lock()
|
||||
defer builtinShadersM.Unlock()
|
||||
|
||||
key := builtinShaderKey{
|
||||
filter: filter,
|
||||
address: address,
|
||||
}
|
||||
if s, ok := builtinShaders[key]; ok {
|
||||
return s
|
||||
}
|
||||
|
||||
src := builtinshader.Shader(filter, address, true)
|
||||
s, err := ebiten.NewShader(src)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("colorm: NewShader for a built-in shader failed: %v", err))
|
||||
}
|
||||
shader := s
|
||||
|
||||
builtinShaders[key] = shader
|
||||
return shader
|
||||
}
|
@ -12,20 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten_test
|
||||
package colorm_test
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
)
|
||||
|
||||
func TestColorMInit(t *testing.T) {
|
||||
var m ebiten.ColorM
|
||||
for i := 0; i < ebiten.ColorMDim-1; i++ {
|
||||
for j := 0; j < ebiten.ColorMDim; j++ {
|
||||
var m colorm.ColorM
|
||||
for i := 0; i < colorm.Dim-1; i++ {
|
||||
for j := 0; j < colorm.Dim; j++ {
|
||||
got := m.Element(i, j)
|
||||
want := 0.0
|
||||
if i == j {
|
||||
@ -38,8 +38,8 @@ func TestColorMInit(t *testing.T) {
|
||||
}
|
||||
|
||||
m.SetElement(0, 0, 1)
|
||||
for i := 0; i < ebiten.ColorMDim-1; i++ {
|
||||
for j := 0; j < ebiten.ColorMDim; j++ {
|
||||
for i := 0; i < colorm.Dim-1; i++ {
|
||||
for j := 0; j < colorm.Dim; j++ {
|
||||
got := m.Element(i, j)
|
||||
want := 0.0
|
||||
if i == j {
|
||||
@ -53,7 +53,7 @@ func TestColorMInit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestColorMAssign(t *testing.T) {
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
m.SetElement(0, 0, 1)
|
||||
m2 := m
|
||||
m.SetElement(0, 0, 0)
|
||||
@ -71,7 +71,7 @@ func TestColorMTranslate(t *testing.T) {
|
||||
{0, 0, 1, 0, 2.5},
|
||||
{0, 0, 0, 1, 3.5},
|
||||
}
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
m.Translate(0.5, 1.5, 2.5, 3.5)
|
||||
for i := 0; i < 4; i++ {
|
||||
for j := 0; j < 5; j++ {
|
||||
@ -91,7 +91,7 @@ func TestColorMScale(t *testing.T) {
|
||||
{0, 0, 2.5, 0, 0},
|
||||
{0, 0, 0, 3.5, 0},
|
||||
}
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
m.Scale(0.5, 1.5, 2.5, 3.5)
|
||||
for i := 0; i < 4; i++ {
|
||||
for j := 0; j < 5; j++ {
|
||||
@ -111,7 +111,7 @@ func TestColorMTranslateAndScale(t *testing.T) {
|
||||
{0, 0, 1, 0, 0},
|
||||
{0, 0, 0, 0.5, 0.5},
|
||||
}
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
m.Translate(0, 0, 0, 1)
|
||||
m.Scale(1, 1, 1, 0.5)
|
||||
for i := 0; i < 4; i++ {
|
||||
@ -132,7 +132,7 @@ func TestColorMMonochrome(t *testing.T) {
|
||||
{0.2990, 0.5870, 0.1140, 0, 0},
|
||||
{0, 0, 0, 1, 0},
|
||||
}
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
m.ChangeHSV(0, 0, 1)
|
||||
for i := 0; i < 4; i++ {
|
||||
for j := 0; j < 5; j++ {
|
||||
@ -152,7 +152,7 @@ func TestColorMConcatSelf(t *testing.T) {
|
||||
{30, 43, 51, 39, 34},
|
||||
{25, 37, 39, 46, 36},
|
||||
}
|
||||
m := ebiten.ColorM{}
|
||||
m := colorm.ColorM{}
|
||||
for i := 0; i < 4; i++ {
|
||||
for j := 0; j < 5; j++ {
|
||||
m.SetElement(i, j, float64((i+j)%5+1))
|
||||
@ -178,23 +178,23 @@ func absDiffU32(x, y uint32) uint32 {
|
||||
}
|
||||
|
||||
func TestColorMApply(t *testing.T) {
|
||||
mono := ebiten.ColorM{}
|
||||
mono := colorm.ColorM{}
|
||||
mono.ChangeHSV(0, 0, 1)
|
||||
|
||||
shiny := ebiten.ColorM{}
|
||||
shiny := colorm.ColorM{}
|
||||
shiny.Translate(1, 1, 1, 0)
|
||||
|
||||
shift := ebiten.ColorM{}
|
||||
shift := colorm.ColorM{}
|
||||
shift.Translate(0.5, 0.5, 0.5, 0.5)
|
||||
|
||||
cases := []struct {
|
||||
ColorM ebiten.ColorM
|
||||
ColorM colorm.ColorM
|
||||
In color.Color
|
||||
Out color.Color
|
||||
Delta uint32
|
||||
}{
|
||||
{
|
||||
ColorM: ebiten.ColorM{},
|
||||
ColorM: colorm.ColorM{},
|
||||
In: color.RGBA{1, 2, 3, 4},
|
||||
Out: color.RGBA{1, 2, 3, 4},
|
||||
Delta: 0x101,
|
||||
@ -237,7 +237,7 @@ func TestColorMApply(t *testing.T) {
|
||||
|
||||
// #1765
|
||||
func TestColorMConcat(t *testing.T) {
|
||||
var a, b ebiten.ColorM
|
||||
var a, b colorm.ColorM
|
||||
a.SetElement(1, 2, -1)
|
||||
a.Concat(b)
|
||||
if got, want := a.Element(1, 2), -1.0; got != want {
|
121
colorm/draw.go
Normal file
121
colorm/draw.go
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2022 The Ebitengine 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 colorm
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
|
||||
)
|
||||
|
||||
// DrawImageOptions represents options for DrawImage.
|
||||
type DrawImageOptions struct {
|
||||
// GeoM is a geometry matrix to draw.
|
||||
// The default (zero) value is identity, which draws the image at (0, 0).
|
||||
GeoM ebiten.GeoM
|
||||
|
||||
// Blend is a blending way of the source color and the destination color.
|
||||
// The default (zero) value is the regular alpha blending.
|
||||
Blend ebiten.Blend
|
||||
|
||||
// Filter is a type of texture filter.
|
||||
// The default (zero) value is ebiten.FilterNearest.
|
||||
Filter ebiten.Filter
|
||||
}
|
||||
|
||||
// DrawImage draws src onto dst.
|
||||
//
|
||||
// DrawImage is basically the same as ebiten.DrawImage, but with a color matrix.
|
||||
func DrawImage(dst, src *ebiten.Image, colorM ColorM, op *DrawImageOptions) {
|
||||
if op == nil {
|
||||
op = &DrawImageOptions{}
|
||||
}
|
||||
|
||||
w, h := src.Size()
|
||||
opShader := &ebiten.DrawRectShaderOptions{}
|
||||
opShader.GeoM = op.GeoM
|
||||
opShader.CompositeMode = ebiten.CompositeModeCustom
|
||||
opShader.Blend = op.Blend
|
||||
opShader.Uniforms = uniforms(colorM)
|
||||
opShader.Images[0] = src
|
||||
s := builtinShader(builtinshader.Filter(op.Filter), builtinshader.AddressUnsafe)
|
||||
dst.DrawRectShader(w, h, s, opShader)
|
||||
}
|
||||
|
||||
// DrawTrianglesOptions represents options for DrawTriangles.
|
||||
type DrawTrianglesOptions struct {
|
||||
// ColorScaleMode is the mode of color scales in vertices.
|
||||
// The default (zero) value is ebiten.ColorScaleModeStraightAlpha.
|
||||
ColorScaleMode ebiten.ColorScaleMode
|
||||
|
||||
// Blend is a blending way of the source color and the destination color.
|
||||
// The default (zero) value is the regular alpha blending.
|
||||
Blend ebiten.Blend
|
||||
|
||||
// Filter is a type of texture filter.
|
||||
// The default (zero) value is ebiten.FilterNearest.
|
||||
Filter ebiten.Filter
|
||||
|
||||
// Address is a sampler address mode.
|
||||
// The default (zero) value is ebiten.AddressUnsafe.
|
||||
Address ebiten.Address
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rule EvenOdd is useful when you want to render a complex polygon.
|
||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
// The default (zero) value is ebiten.FillAll.
|
||||
FillRule ebiten.FillRule
|
||||
|
||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||
// AntiAlias is useful especially when you pass vertices from the vector package.
|
||||
//
|
||||
// AntiAlias increases internal draw calls and might affect performance.
|
||||
// Use the build tag `ebitenginedebug` to check the number of draw calls if you care.
|
||||
//
|
||||
// The default (zero) value is false.
|
||||
AntiAlias bool
|
||||
}
|
||||
|
||||
// DrawTriangles draws triangles onto dst.
|
||||
//
|
||||
// DrawTriangles is basically the same as ebiten.DrawTriangles, but with a color matrix.
|
||||
func DrawTriangles(dst *ebiten.Image, vertices []ebiten.Vertex, indices []uint16, img *ebiten.Image, colorM ColorM, op *DrawTrianglesOptions) {
|
||||
if op == nil {
|
||||
op = &DrawTrianglesOptions{}
|
||||
}
|
||||
|
||||
if op.ColorScaleMode == ebiten.ColorScaleModeStraightAlpha {
|
||||
vs := make([]ebiten.Vertex, len(vertices))
|
||||
copy(vs, vertices)
|
||||
for i := range vertices {
|
||||
vs[i].ColorR *= vs[i].ColorA
|
||||
vs[i].ColorG *= vs[i].ColorA
|
||||
vs[i].ColorB *= vs[i].ColorA
|
||||
}
|
||||
vertices = vs
|
||||
}
|
||||
|
||||
opShader := &ebiten.DrawTrianglesShaderOptions{}
|
||||
opShader.CompositeMode = ebiten.CompositeModeCustom
|
||||
opShader.Blend = op.Blend
|
||||
opShader.FillRule = op.FillRule
|
||||
opShader.AntiAlias = op.AntiAlias
|
||||
opShader.Uniforms = uniforms(colorM)
|
||||
opShader.Images[0] = img
|
||||
s := builtinShader(builtinshader.Filter(op.Filter), builtinshader.Address(op.Address))
|
||||
dst.DrawTrianglesShader(vertices, indices, s, opShader)
|
||||
}
|
301
colorm/draw_test.go
Normal file
301
colorm/draw_test.go
Normal file
@ -0,0 +1,301 @@
|
||||
// Copyright 2022 The Ebitengine 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 colorm_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
t "github.com/hajimehoshi/ebiten/v2/internal/testing"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
)
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// sameColors compares c1 and c2 and returns a boolean value indicating
|
||||
// if the two colors are (almost) same.
|
||||
//
|
||||
// Pixels read from GPU might include errors (#492), and
|
||||
// sameColors considers such errors as delta.
|
||||
func sameColors(c1, c2 color.RGBA, delta int) bool {
|
||||
return abs(int(c1.R)-int(c2.R)) <= delta &&
|
||||
abs(int(c1.G)-int(c2.G)) <= delta &&
|
||||
abs(int(c1.B)-int(c2.B)) <= delta &&
|
||||
abs(int(c1.A)-int(c2.A)) <= delta
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
ui.SetPanicOnErrorOnReadingPixelsForTesting(true)
|
||||
t.MainWithRunLoop(m)
|
||||
}
|
||||
|
||||
func TestDrawTrianglesWithColorM(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
dst0 := ebiten.NewImage(w, h)
|
||||
src := ebiten.NewImage(w, h)
|
||||
src.Fill(color.White)
|
||||
|
||||
vs0 := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: 0,
|
||||
SrcX: 0,
|
||||
SrcY: 0,
|
||||
ColorR: 1,
|
||||
ColorG: 1,
|
||||
ColorB: 1,
|
||||
ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: w,
|
||||
DstY: 0,
|
||||
SrcX: w,
|
||||
SrcY: 0,
|
||||
ColorR: 1,
|
||||
ColorG: 1,
|
||||
ColorB: 1,
|
||||
ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: h,
|
||||
SrcX: 0,
|
||||
SrcY: h,
|
||||
ColorR: 1,
|
||||
ColorG: 1,
|
||||
ColorB: 1,
|
||||
ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: w,
|
||||
DstY: h,
|
||||
SrcX: w,
|
||||
SrcY: h,
|
||||
ColorR: 1,
|
||||
ColorG: 1,
|
||||
ColorB: 1,
|
||||
ColorA: 1,
|
||||
},
|
||||
}
|
||||
|
||||
var cm colorm.ColorM
|
||||
cm.Scale(0.2, 0.4, 0.6, 0.8)
|
||||
op := &colorm.DrawTrianglesOptions{}
|
||||
is := []uint16{0, 1, 2, 1, 2, 3}
|
||||
colorm.DrawTriangles(dst0, vs0, is, src, cm, op)
|
||||
|
||||
for _, format := range []ebiten.ColorScaleMode{
|
||||
ebiten.ColorScaleModeStraightAlpha,
|
||||
ebiten.ColorScaleModePremultipliedAlpha,
|
||||
} {
|
||||
format := format
|
||||
t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
|
||||
var cr, cg, cb, ca float32
|
||||
switch format {
|
||||
case ebiten.ColorScaleModeStraightAlpha:
|
||||
// The values are the same as ColorM.Scale
|
||||
cr = 0.2
|
||||
cg = 0.4
|
||||
cb = 0.6
|
||||
ca = 0.8
|
||||
case ebiten.ColorScaleModePremultipliedAlpha:
|
||||
cr = 0.2 * 0.8
|
||||
cg = 0.4 * 0.8
|
||||
cb = 0.6 * 0.8
|
||||
ca = 0.8
|
||||
}
|
||||
vs1 := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: 0,
|
||||
SrcX: 0,
|
||||
SrcY: 0,
|
||||
ColorR: cr,
|
||||
ColorG: cg,
|
||||
ColorB: cb,
|
||||
ColorA: ca,
|
||||
},
|
||||
{
|
||||
DstX: w,
|
||||
DstY: 0,
|
||||
SrcX: w,
|
||||
SrcY: 0,
|
||||
ColorR: cr,
|
||||
ColorG: cg,
|
||||
ColorB: cb,
|
||||
ColorA: ca,
|
||||
},
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: h,
|
||||
SrcX: 0,
|
||||
SrcY: h,
|
||||
ColorR: cr,
|
||||
ColorG: cg,
|
||||
ColorB: cb,
|
||||
ColorA: ca,
|
||||
},
|
||||
{
|
||||
DstX: w,
|
||||
DstY: h,
|
||||
SrcX: w,
|
||||
SrcY: h,
|
||||
ColorR: cr,
|
||||
ColorG: cg,
|
||||
ColorB: cb,
|
||||
ColorA: ca,
|
||||
},
|
||||
}
|
||||
|
||||
dst1 := ebiten.NewImage(w, h)
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.ColorScaleMode = format
|
||||
dst1.DrawTriangles(vs1, is, src, op)
|
||||
|
||||
for j := 0; j < h; j++ {
|
||||
for i := 0; i < w; i++ {
|
||||
got := dst0.At(i, j)
|
||||
want := dst1.At(i, j)
|
||||
if got != want {
|
||||
t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorMAndScale(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
src := ebiten.NewImage(w, h)
|
||||
|
||||
src.Fill(color.RGBA{0x80, 0x80, 0x80, 0x80})
|
||||
vs := []ebiten.Vertex{
|
||||
{
|
||||
SrcX: 0,
|
||||
SrcY: 0,
|
||||
DstX: 0,
|
||||
DstY: 0,
|
||||
ColorR: 0.5,
|
||||
ColorG: 0.25,
|
||||
ColorB: 0.5,
|
||||
ColorA: 0.75,
|
||||
},
|
||||
{
|
||||
SrcX: w,
|
||||
SrcY: 0,
|
||||
DstX: w,
|
||||
DstY: 0,
|
||||
ColorR: 0.5,
|
||||
ColorG: 0.25,
|
||||
ColorB: 0.5,
|
||||
ColorA: 0.75,
|
||||
},
|
||||
{
|
||||
SrcX: 0,
|
||||
SrcY: h,
|
||||
DstX: 0,
|
||||
DstY: h,
|
||||
ColorR: 0.5,
|
||||
ColorG: 0.25,
|
||||
ColorB: 0.5,
|
||||
ColorA: 0.75,
|
||||
},
|
||||
{
|
||||
SrcX: w,
|
||||
SrcY: h,
|
||||
DstX: w,
|
||||
DstY: h,
|
||||
ColorR: 0.5,
|
||||
ColorG: 0.25,
|
||||
ColorB: 0.5,
|
||||
ColorA: 0.75,
|
||||
},
|
||||
}
|
||||
is := []uint16{0, 1, 2, 1, 2, 3}
|
||||
|
||||
for _, format := range []ebiten.ColorScaleMode{
|
||||
ebiten.ColorScaleModeStraightAlpha,
|
||||
ebiten.ColorScaleModePremultipliedAlpha,
|
||||
} {
|
||||
format := format
|
||||
t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
|
||||
dst := ebiten.NewImage(w, h)
|
||||
|
||||
var cm colorm.ColorM
|
||||
cm.Translate(0.25, 0.25, 0.25, 0)
|
||||
op := &colorm.DrawTrianglesOptions{}
|
||||
op.ColorScaleMode = format
|
||||
colorm.DrawTriangles(dst, vs, is, src, cm, op)
|
||||
|
||||
got := dst.At(0, 0).(color.RGBA)
|
||||
alphaBeforeScale := 0.5
|
||||
var want color.RGBA
|
||||
switch format {
|
||||
case ebiten.ColorScaleModeStraightAlpha:
|
||||
want = color.RGBA{
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25 * 0.75)),
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
|
||||
byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
|
||||
}
|
||||
case ebiten.ColorScaleModePremultipliedAlpha:
|
||||
want = color.RGBA{
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25)),
|
||||
byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
|
||||
byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
|
||||
}
|
||||
}
|
||||
if !sameColors(got, want, 2) {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #1213
|
||||
func TestColorMCopy(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
dst := ebiten.NewImage(w, h)
|
||||
src := ebiten.NewImage(w, h)
|
||||
|
||||
for k := 0; k < 256; k++ {
|
||||
var cm colorm.ColorM
|
||||
cm.Translate(1, 1, 1, float64(k)/0xff)
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.Blend = ebiten.BlendCopy
|
||||
colorm.DrawImage(dst, src, cm, op)
|
||||
|
||||
for j := 0; j < h; j++ {
|
||||
for i := 0; i < w; i++ {
|
||||
got := dst.At(i, j).(color.RGBA)
|
||||
want := color.RGBA{byte(k), byte(k), byte(k), byte(k)}
|
||||
if !sameColors(got, want, 1) {
|
||||
t.Fatalf("dst.At(%d, %d), k: %d: got %v, want %v", i, j, k, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
// ColorScale represents a scale of RGBA color.
|
||||
// ColorScale is intended to be applied to a premultiplied-alpha color value.
|
||||
//
|
||||
// The initial (zero) value of ColorScale is an identity scale (1, 1, 1, 1).
|
||||
type ColorScale struct {
|
||||
@ -33,6 +34,14 @@ func (c *ColorScale) String() string {
|
||||
return fmt.Sprintf("(%f,%f,%f,%f)", c.r_1+1, c.g_1+1, c.b_1+1, c.a_1+1)
|
||||
}
|
||||
|
||||
// Reset resets the ColorScale as identity.
|
||||
func (c *ColorScale) Reset() {
|
||||
c.r_1 = 0
|
||||
c.g_1 = 0
|
||||
c.b_1 = 0
|
||||
c.a_1 = 0
|
||||
}
|
||||
|
||||
// R returns the red scale.
|
||||
func (c *ColorScale) R() float32 {
|
||||
return c.r_1 + 1
|
||||
@ -85,8 +94,20 @@ func (c *ColorScale) Scale(r, g, b, a float32) {
|
||||
c.a_1 = (c.a_1+1)*a - 1
|
||||
}
|
||||
|
||||
// ScaleAlpha multiplies the given alpha value to the current scale.
|
||||
func (c *ColorScale) ScaleAlpha(a float32) {
|
||||
c.r_1 = (c.r_1+1)*a - 1
|
||||
c.g_1 = (c.g_1+1)*a - 1
|
||||
c.b_1 = (c.b_1+1)*a - 1
|
||||
c.a_1 = (c.a_1+1)*a - 1
|
||||
}
|
||||
|
||||
// ScaleWithColor multiplies the given color values to the current scale.
|
||||
func (c *ColorScale) ScaleWithColor(clr color.Color) {
|
||||
cr, cg, cb, ca := clr.RGBA()
|
||||
c.Scale(float32(cr)/0xffff, float32(cg)/0xffff, float32(cb)/0xffff, float32(ca)/0xffff)
|
||||
}
|
||||
|
||||
func (c *ColorScale) apply(r, g, b, a float32) (float32, float32, float32, float32) {
|
||||
return (c.r_1 + 1) * r, (c.g_1 + 1) * g, (c.b_1 + 1) * b, (c.a_1 + 1) * a
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ func (b *Board) Draw(boardImage *ebiten.Image) {
|
||||
x := i*tileSize + (i+1)*tileMargin
|
||||
y := j*tileSize + (j+1)*tileMargin
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
op.ColorM.ScaleWithColor(tileBackgroundColor(v))
|
||||
op.ColorScale.ScaleWithColor(tileBackgroundColor(v))
|
||||
boardImage.DrawImage(tileImage, op)
|
||||
}
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ func (t *Tile) Draw(boardImage *ebiten.Image) {
|
||||
op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
|
||||
}
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
op.ColorM.ScaleWithColor(tileBackgroundColor(v))
|
||||
op.ColorScale.ScaleWithColor(tileBackgroundColor(v))
|
||||
boardImage.DrawImage(tileImage, op)
|
||||
str := strconv.Itoa(v)
|
||||
|
||||
|
@ -61,7 +61,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// Draw 100 Ebitens
|
||||
v := g.offset()
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(1.0, 1.0, 1.0, 0.5)
|
||||
op.ColorScale.ScaleAlpha(0.5)
|
||||
for i := 0; i < 10*10; i++ {
|
||||
op.GeoM.Reset()
|
||||
x := float64(i%10)*v + 15
|
||||
|
@ -16,6 +16,7 @@ package blocks
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
)
|
||||
|
||||
const maxFlushCount = 20
|
||||
@ -193,8 +194,8 @@ func min(a, b float64) float64 {
|
||||
return a
|
||||
}
|
||||
|
||||
func flushingColor(rate float64) ebiten.ColorM {
|
||||
clr := ebiten.ColorM{}
|
||||
func flushingColor(rate float64) colorm.ColorM {
|
||||
var clr colorm.ColorM
|
||||
alpha := min(1, rate*2)
|
||||
clr.Scale(1, 1, 1, alpha)
|
||||
r := min(1, (1-rate)*2)
|
||||
@ -211,7 +212,7 @@ func (f *Field) Draw(r *ebiten.Image, x, y int) {
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < fieldBlockCountX; i++ {
|
||||
drawBlock(r, f.blocks[i][j], i*blockWidth+x, j*blockHeight+y, ebiten.ColorM{})
|
||||
drawBlock(r, f.blocks[i][j], i*blockWidth+x, j*blockHeight+y, colorm.ColorM{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
@ -148,17 +149,17 @@ func NewGameScene() *GameScene {
|
||||
}
|
||||
|
||||
var (
|
||||
lightGray ebiten.ColorM
|
||||
lightGray colorm.ColorM
|
||||
)
|
||||
|
||||
func init() {
|
||||
id := ebiten.ColorM{}
|
||||
var id colorm.ColorM
|
||||
|
||||
mono := ebiten.ColorM{}
|
||||
var mono colorm.ColorM
|
||||
mono.ChangeHSV(0, 0, 1)
|
||||
|
||||
for j := 0; j < ebiten.ColorMDim-1; j++ {
|
||||
for i := 0; i < ebiten.ColorMDim-1; i++ {
|
||||
for j := 0; j < colorm.Dim-1; j++ {
|
||||
for i := 0; i < colorm.Dim-1; i++ {
|
||||
lightGray.SetElement(i, j, mono.Element(i, j)*0.7+id.Element(i, j)*0.3)
|
||||
}
|
||||
}
|
||||
@ -177,13 +178,12 @@ func (s *GameScene) drawBackground(r *ebiten.Image) {
|
||||
scale = scaleH
|
||||
}
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
|
||||
op.GeoM.Scale(scale, scale)
|
||||
op.GeoM.Translate(ScreenWidth/2, ScreenHeight/2)
|
||||
op.ColorM = lightGray
|
||||
op.Filter = ebiten.FilterLinear
|
||||
r.DrawImage(imageGameBG, op)
|
||||
colorm.DrawImage(r, imageGameBG, lightGray, op)
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
_ "image/png"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
rblocks "github.com/hajimehoshi/ebiten/v2/examples/resources/images/blocks"
|
||||
)
|
||||
|
||||
@ -165,17 +166,16 @@ const (
|
||||
fieldBlockCountY = 20
|
||||
)
|
||||
|
||||
func drawBlock(r *ebiten.Image, block BlockType, x, y int, clr ebiten.ColorM) {
|
||||
func drawBlock(r *ebiten.Image, block BlockType, x, y int, clr colorm.ColorM) {
|
||||
if block == BlockTypeNone {
|
||||
return
|
||||
}
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM = clr
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
|
||||
srcX := (int(block) - 1) * blockWidth
|
||||
r.DrawImage(imageBlocks.SubImage(image.Rect(srcX, 0, srcX+blockWidth, blockHeight)).(*ebiten.Image), op)
|
||||
colorm.DrawImage(r, imageBlocks.SubImage(image.Rect(srcX, 0, srcX+blockWidth, blockHeight)).(*ebiten.Image), clr, op)
|
||||
}
|
||||
|
||||
func (p *Piece) InitialPosition() (int, int) {
|
||||
@ -249,7 +249,7 @@ func (p *Piece) Draw(r *ebiten.Image, x, y int, angle Angle) {
|
||||
for i := range p.blocks {
|
||||
for j := range p.blocks[i] {
|
||||
if p.isBlocked(i, j, angle) {
|
||||
drawBlock(r, p.blockType, i*blockWidth+x, j*blockHeight+y, ebiten.ColorM{})
|
||||
drawBlock(r, p.blockType, i*blockWidth+x, j*blockHeight+y, colorm.ColorM{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +73,9 @@ func (s *SceneManager) Draw(r *ebiten.Image) {
|
||||
|
||||
r.DrawImage(transitionFrom, nil)
|
||||
|
||||
alpha := 1 - float64(s.transitionCount)/float64(transitionMaxCount)
|
||||
alpha := 1 - float32(s.transitionCount)/float32(transitionMaxCount)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(1, 1, 1, alpha)
|
||||
op.ColorScale.ScaleAlpha(alpha)
|
||||
r.DrawImage(transitionTo, op)
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// A_{n+1} = A_n * (1 - 1/(n+1)) + a_{n+1} * 1/(n+1)
|
||||
// which is precisely what an alpha blend with alpha 1/(n+1) does.
|
||||
layers++
|
||||
op.ColorM.Scale(1, 1, 1, 1.0/float64(layers))
|
||||
op.ColorScale.ScaleAlpha(1 / float32(layers))
|
||||
screen.DrawImage(gophersImage, op)
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
screen.Fill(color.Black)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(200.0/255.0, 200.0/255.0, 200.0/255.0, 1)
|
||||
op.ColorScale.Scale(200.0/255.0, 200.0/255.0, 200.0/255.0, 1)
|
||||
|
||||
g.space.EachBody(func(body *cp.Body) {
|
||||
op.GeoM.Reset()
|
||||
|
@ -77,10 +77,10 @@ func (s *Sprite) MoveBy(x, y int) {
|
||||
}
|
||||
|
||||
// Draw draws the sprite.
|
||||
func (s *Sprite) Draw(screen *ebiten.Image, dx, dy int, alpha float64) {
|
||||
func (s *Sprite) Draw(screen *ebiten.Image, dx, dy int, alpha float32) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(s.x+dx), float64(s.y+dy))
|
||||
op.ColorM.Scale(1, 1, 1, alpha)
|
||||
op.ColorScale.ScaleAlpha(alpha)
|
||||
screen.DrawImage(s.image, op)
|
||||
screen.DrawImage(s.image, op)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
)
|
||||
|
||||
@ -66,20 +67,21 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
// Fill with solid colors
|
||||
for i, c := range colors {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op := &colorm.DrawImageOptions{}
|
||||
x := i % 4
|
||||
y := i/4 + 1
|
||||
op.GeoM.Translate(ox+float64(dx*x), oy+float64(dy*y))
|
||||
|
||||
// Reset RGB (not Alpha) 0 forcibly
|
||||
op.ColorM.Scale(0, 0, 0, 1)
|
||||
var cm colorm.ColorM
|
||||
cm.Scale(0, 0, 0, 1)
|
||||
|
||||
// Set color
|
||||
r := float64(c.R) / 0xff
|
||||
g := float64(c.G) / 0xff
|
||||
b := float64(c.B) / 0xff
|
||||
op.ColorM.Translate(r, g, b, 0)
|
||||
screen.DrawImage(ebitenImage, op)
|
||||
cm.Translate(r, g, b, 0)
|
||||
colorm.DrawImage(screen, ebitenImage, cm, op)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
@ -100,7 +101,7 @@ func (g *Game) Update() error {
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// Center the image on the screen.
|
||||
w, h := gophersImage.Size()
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
|
||||
op.GeoM.Scale(2, 2)
|
||||
op.GeoM.Translate(float64(screenWidth)/2, float64(screenHeight)/2)
|
||||
@ -109,15 +110,16 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
hue := float64(g.hue128) * 2 * math.Pi / 128
|
||||
saturation := float64(g.saturation128) / 128
|
||||
value := float64(g.value128) / 128
|
||||
op.ColorM.ChangeHSV(hue, saturation, value)
|
||||
var c colorm.ColorM
|
||||
c.ChangeHSV(hue, saturation, value)
|
||||
|
||||
// Invert the color.
|
||||
if g.inverted {
|
||||
op.ColorM.Scale(-1, -1, -1, 1)
|
||||
op.ColorM.Translate(1, 1, 1, 0)
|
||||
c.Scale(-1, -1, -1, 1)
|
||||
c.Translate(1, 1, 1, 0)
|
||||
}
|
||||
|
||||
screen.DrawImage(gophersImage, op)
|
||||
colorm.DrawImage(screen, gophersImage, c, op)
|
||||
|
||||
// Draw the text of the current status.
|
||||
msgInverted := "false"
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
)
|
||||
|
||||
@ -46,15 +47,15 @@ func (g *Game) Update() error {
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// Center the image on the screen.
|
||||
w, h := gophersImage.Size()
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
|
||||
op.GeoM.Scale(2, 2)
|
||||
op.GeoM.Translate(float64(screenWidth)/2, float64(screenHeight)/2)
|
||||
|
||||
// Rotate the hue.
|
||||
op.ColorM.RotateHue(float64(g.count%360) * 2 * math.Pi / 360)
|
||||
|
||||
screen.DrawImage(gophersImage, op)
|
||||
var c colorm.ColorM
|
||||
c.RotateHue(float64(g.count%360) * 2 * math.Pi / 360)
|
||||
colorm.DrawImage(screen, gophersImage, c, op)
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
|
@ -62,7 +62,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// Draw the base (grayed) keyboard image.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(offsetX, offsetY)
|
||||
op.ColorM.Scale(0.5, 0.5, 0.5, 1)
|
||||
op.ColorScale.Scale(0.5, 0.5, 0.5, 1)
|
||||
screen.DrawImage(keyboardImage, op)
|
||||
|
||||
// Draw the highlighted keys.
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
@ -115,14 +116,15 @@ func (g *Game) Update() error {
|
||||
|
||||
// paint draws the brush on the given canvas image at the position (x, y).
|
||||
func (g *Game) paint(canvas *ebiten.Image, x, y int) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op := &colorm.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
var cm colorm.ColorM
|
||||
// Scale the color and rotate the hue so that colors vary on each frame.
|
||||
op.ColorM.Scale(1.0, 0.50, 0.125, 1.0)
|
||||
cm.Scale(1.0, 0.50, 0.125, 1.0)
|
||||
tps := ebiten.TPS()
|
||||
theta := 2.0 * math.Pi * float64(g.count%tps) / float64(tps)
|
||||
op.ColorM.RotateHue(theta)
|
||||
canvas.DrawImage(brushImage, op)
|
||||
cm.RotateHue(theta)
|
||||
colorm.DrawImage(canvas, brushImage, cm, op)
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
@ -59,7 +59,7 @@ type sprite struct {
|
||||
img *ebiten.Image
|
||||
scale float64
|
||||
angle float64
|
||||
alpha float64
|
||||
alpha float32
|
||||
}
|
||||
|
||||
func (s *sprite) update() {
|
||||
@ -94,8 +94,8 @@ func (s *sprite) draw(screen *ebiten.Image) {
|
||||
op.GeoM.Translate(x, y)
|
||||
op.GeoM.Translate(ox, oy)
|
||||
|
||||
rate := float64(s.count) / float64(s.maxCount)
|
||||
alpha := 0.0
|
||||
rate := float32(s.count) / float32(s.maxCount)
|
||||
var alpha float32
|
||||
if rate < 0.2 {
|
||||
alpha = rate / 0.2
|
||||
} else if rate > 0.8 {
|
||||
@ -104,7 +104,7 @@ func (s *sprite) draw(screen *ebiten.Image) {
|
||||
alpha = 1
|
||||
}
|
||||
alpha *= s.alpha
|
||||
op.ColorM.Scale(1, 1, 1, alpha)
|
||||
op.ColorScale.ScaleAlpha(alpha)
|
||||
|
||||
screen.DrawImage(s.img, op)
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
// Draw shadow
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(1, 1, 1, 0.7)
|
||||
op.ColorScale.ScaleAlpha(0.7)
|
||||
screen.DrawImage(shadowImage, op)
|
||||
|
||||
// Draw walls
|
||||
|
@ -50,7 +50,7 @@ func init() {
|
||||
ebitenImage = ebiten.NewImage(w, h)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(1, 1, 1, 0.5)
|
||||
op.ColorScale.ScaleAlpha(0.5)
|
||||
ebitenImage.DrawImage(origEbitenImage, op)
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func init() {
|
||||
ebitenImage = ebiten.NewImage(w, h)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(1, 1, 1, 0.5)
|
||||
op.ColorScale.ScaleAlpha(0.5)
|
||||
ebitenImage.DrawImage(origEbitenImage, op)
|
||||
}
|
||||
|
||||
|
@ -127,20 +127,20 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(x, y)
|
||||
op.GeoM.Translate(gl.X, gl.Y)
|
||||
op.ColorM.Reset()
|
||||
r := 1.0
|
||||
op.ColorScale.Reset()
|
||||
r := float32(1)
|
||||
if i%3 == 0 {
|
||||
r = 0.5
|
||||
}
|
||||
g := 1.0
|
||||
g := float32(1)
|
||||
if i%3 == 1 {
|
||||
g = 0.5
|
||||
}
|
||||
b := 1.0
|
||||
b := float32(1)
|
||||
if i%3 == 2 {
|
||||
b = 0.5
|
||||
}
|
||||
op.ColorM.Scale(r, g, b, 1)
|
||||
op.ColorScale.Scale(r, g, b, 1)
|
||||
screen.DrawImage(gl.Image, op)
|
||||
}
|
||||
}
|
||||
|
12
image.go
12
image.go
@ -102,8 +102,16 @@ type DrawImageOptions struct {
|
||||
// The default (zero) value is identity, which draws the image at (0, 0).
|
||||
GeoM GeoM
|
||||
|
||||
// ColorScale is a scale of color.
|
||||
// ColorScale is slightly different from ColorM's Scale in terms of alphas:
|
||||
// ColorScale is applied to premultiplied-alpha colors, while ColorM is applied to straight-alpha colors.
|
||||
// The default (zero) value is identity, which is (1, 1, 1, 1).
|
||||
ColorScale ColorScale
|
||||
|
||||
// ColorM is a color matrix to draw.
|
||||
// The default (zero) value is identity, which doesn't change any color.
|
||||
//
|
||||
// Deprecated: as of v2.5. Use ColorScale or the package colorm instead.
|
||||
ColorM ColorM
|
||||
|
||||
// CompositeMode is a composite mode to draw.
|
||||
@ -231,6 +239,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
||||
sx0, sy0 := img.adjustPosition(bounds.Min.X, bounds.Min.Y)
|
||||
sx1, sy1 := img.adjustPosition(bounds.Max.X, bounds.Max.Y)
|
||||
colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM())
|
||||
cr, cg, cb, ca = options.ColorScale.apply(cr, cg, cb, ca)
|
||||
vs := graphics.QuadVertices(float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||
is := graphics.QuadIndices()
|
||||
|
||||
@ -324,9 +333,12 @@ type DrawTrianglesOptions struct {
|
||||
// ColorM is a color matrix to draw.
|
||||
// The default (zero) value is identity, which doesn't change any color.
|
||||
// ColorM is applied before vertex color scale is applied.
|
||||
//
|
||||
// Deprecated: as of v2.5. Use the package colorm instead.
|
||||
ColorM ColorM
|
||||
|
||||
// ColorScaleMode is the mode of color scales in vertices.
|
||||
// ColorScaleMode affects the color calculation with vertex colors, but doesn't affect with a color matrix.
|
||||
// The default (zero) value is ColorScaleModeStraightAlpha.
|
||||
ColorScaleMode ColorScaleMode
|
||||
|
||||
|
@ -1009,12 +1009,12 @@ func TestImageMipmapColor(t *testing.T) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.Filter = ebiten.FilterLinear
|
||||
op.GeoM.Scale(s, s)
|
||||
op.ColorM.Scale(1, 1, 0, 1)
|
||||
op.ColorScale.Scale(1, 1, 0, 1)
|
||||
img0.DrawImage(img1, op)
|
||||
|
||||
op.GeoM.Translate(128, 0)
|
||||
op.ColorM.Reset()
|
||||
op.ColorM.Scale(0, 1, 1, 1)
|
||||
op.ColorScale.Reset()
|
||||
op.ColorScale.Scale(0, 1, 1, 1)
|
||||
img0.DrawImage(img1, op)
|
||||
|
||||
want := color.RGBA{0, 0xff, 0xff, 0xff}
|
||||
@ -3967,6 +3967,30 @@ func TestImageColorMScale(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageColorScaleAndColorM(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
dst0 := ebiten.NewImage(w, h)
|
||||
dst1 := ebiten.NewImage(w, h)
|
||||
src := ebiten.NewImage(w, h)
|
||||
src.Fill(color.RGBA{0x24, 0x3f, 0x6a, 0x88})
|
||||
|
||||
// ColorScale is applied to premultiplied-alpha colors.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.ColorScale.Scale(0.3*0.6, 0.4*0.6, 0.5*0.6, 0.6)
|
||||
dst0.DrawImage(src, op)
|
||||
|
||||
// ColorM.Scale is applied to straight-alpha colors.
|
||||
op = &ebiten.DrawImageOptions{}
|
||||
op.ColorM.Scale(0.3, 0.4, 0.5, 0.6)
|
||||
dst1.DrawImage(src, op)
|
||||
|
||||
got := dst0.At(0, 0)
|
||||
want := dst1.At(0, 0)
|
||||
if got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #2428
|
||||
func TestImageSetAndSubImage(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
|
@ -181,7 +181,7 @@ var textM sync.Mutex
|
||||
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
op.ColorM.ScaleWithColor(clr)
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
DrawWithOptions(dst, text, face, op)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user