draw_test.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright 2022 The Ebitengine Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package colorm_test
  15. import (
  16. "fmt"
  17. "image/color"
  18. "math"
  19. "testing"
  20. "github.com/hajimehoshi/ebiten/v2"
  21. "github.com/hajimehoshi/ebiten/v2/colorm"
  22. t "github.com/hajimehoshi/ebiten/v2/internal/testing"
  23. "github.com/hajimehoshi/ebiten/v2/internal/ui"
  24. )
  25. func abs(x int) int {
  26. if x < 0 {
  27. return -x
  28. }
  29. return x
  30. }
  31. // sameColors compares c1 and c2 and returns a boolean value indicating
  32. // if the two colors are (almost) same.
  33. //
  34. // Pixels read from GPU might include errors (#492), and
  35. // sameColors considers such errors as delta.
  36. func sameColors(c1, c2 color.RGBA, delta int) bool {
  37. return abs(int(c1.R)-int(c2.R)) <= delta &&
  38. abs(int(c1.G)-int(c2.G)) <= delta &&
  39. abs(int(c1.B)-int(c2.B)) <= delta &&
  40. abs(int(c1.A)-int(c2.A)) <= delta
  41. }
  42. func TestMain(m *testing.M) {
  43. ui.SetPanicOnErrorOnReadingPixelsForTesting(true)
  44. t.MainWithRunLoop(m)
  45. }
  46. func TestDrawTrianglesWithColorM(t *testing.T) {
  47. const w, h = 16, 16
  48. dst0 := ebiten.NewImage(w, h)
  49. src := ebiten.NewImage(w, h)
  50. src.Fill(color.White)
  51. vs0 := []ebiten.Vertex{
  52. {
  53. DstX: 0,
  54. DstY: 0,
  55. SrcX: 0,
  56. SrcY: 0,
  57. ColorR: 1,
  58. ColorG: 1,
  59. ColorB: 1,
  60. ColorA: 1,
  61. },
  62. {
  63. DstX: w,
  64. DstY: 0,
  65. SrcX: w,
  66. SrcY: 0,
  67. ColorR: 1,
  68. ColorG: 1,
  69. ColorB: 1,
  70. ColorA: 1,
  71. },
  72. {
  73. DstX: 0,
  74. DstY: h,
  75. SrcX: 0,
  76. SrcY: h,
  77. ColorR: 1,
  78. ColorG: 1,
  79. ColorB: 1,
  80. ColorA: 1,
  81. },
  82. {
  83. DstX: w,
  84. DstY: h,
  85. SrcX: w,
  86. SrcY: h,
  87. ColorR: 1,
  88. ColorG: 1,
  89. ColorB: 1,
  90. ColorA: 1,
  91. },
  92. }
  93. var cm colorm.ColorM
  94. cm.Scale(0.2, 0.4, 0.6, 0.8)
  95. op := &colorm.DrawTrianglesOptions{}
  96. is := []uint16{0, 1, 2, 1, 2, 3}
  97. colorm.DrawTriangles(dst0, vs0, is, src, cm, op)
  98. for _, format := range []ebiten.ColorScaleMode{
  99. ebiten.ColorScaleModeStraightAlpha,
  100. ebiten.ColorScaleModePremultipliedAlpha,
  101. } {
  102. format := format
  103. t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
  104. var cr, cg, cb, ca float32
  105. switch format {
  106. case ebiten.ColorScaleModeStraightAlpha:
  107. // The values are the same as ColorM.Scale
  108. cr = 0.2
  109. cg = 0.4
  110. cb = 0.6
  111. ca = 0.8
  112. case ebiten.ColorScaleModePremultipliedAlpha:
  113. cr = 0.2 * 0.8
  114. cg = 0.4 * 0.8
  115. cb = 0.6 * 0.8
  116. ca = 0.8
  117. }
  118. vs1 := []ebiten.Vertex{
  119. {
  120. DstX: 0,
  121. DstY: 0,
  122. SrcX: 0,
  123. SrcY: 0,
  124. ColorR: cr,
  125. ColorG: cg,
  126. ColorB: cb,
  127. ColorA: ca,
  128. },
  129. {
  130. DstX: w,
  131. DstY: 0,
  132. SrcX: w,
  133. SrcY: 0,
  134. ColorR: cr,
  135. ColorG: cg,
  136. ColorB: cb,
  137. ColorA: ca,
  138. },
  139. {
  140. DstX: 0,
  141. DstY: h,
  142. SrcX: 0,
  143. SrcY: h,
  144. ColorR: cr,
  145. ColorG: cg,
  146. ColorB: cb,
  147. ColorA: ca,
  148. },
  149. {
  150. DstX: w,
  151. DstY: h,
  152. SrcX: w,
  153. SrcY: h,
  154. ColorR: cr,
  155. ColorG: cg,
  156. ColorB: cb,
  157. ColorA: ca,
  158. },
  159. }
  160. dst1 := ebiten.NewImage(w, h)
  161. op := &ebiten.DrawTrianglesOptions{}
  162. op.ColorScaleMode = format
  163. dst1.DrawTriangles(vs1, is, src, op)
  164. for j := 0; j < h; j++ {
  165. for i := 0; i < w; i++ {
  166. got := dst0.At(i, j)
  167. want := dst1.At(i, j)
  168. if got != want {
  169. t.Errorf("At(%d, %d): got: %v, want: %v", i, j, got, want)
  170. }
  171. }
  172. }
  173. })
  174. }
  175. }
  176. func TestColorMAndScale(t *testing.T) {
  177. const w, h = 16, 16
  178. src := ebiten.NewImage(w, h)
  179. src.Fill(color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80})
  180. vs := []ebiten.Vertex{
  181. {
  182. SrcX: 0,
  183. SrcY: 0,
  184. DstX: 0,
  185. DstY: 0,
  186. ColorR: 0.5,
  187. ColorG: 0.25,
  188. ColorB: 0.5,
  189. ColorA: 0.75,
  190. },
  191. {
  192. SrcX: w,
  193. SrcY: 0,
  194. DstX: w,
  195. DstY: 0,
  196. ColorR: 0.5,
  197. ColorG: 0.25,
  198. ColorB: 0.5,
  199. ColorA: 0.75,
  200. },
  201. {
  202. SrcX: 0,
  203. SrcY: h,
  204. DstX: 0,
  205. DstY: h,
  206. ColorR: 0.5,
  207. ColorG: 0.25,
  208. ColorB: 0.5,
  209. ColorA: 0.75,
  210. },
  211. {
  212. SrcX: w,
  213. SrcY: h,
  214. DstX: w,
  215. DstY: h,
  216. ColorR: 0.5,
  217. ColorG: 0.25,
  218. ColorB: 0.5,
  219. ColorA: 0.75,
  220. },
  221. }
  222. is := []uint16{0, 1, 2, 1, 2, 3}
  223. for _, format := range []ebiten.ColorScaleMode{
  224. ebiten.ColorScaleModeStraightAlpha,
  225. ebiten.ColorScaleModePremultipliedAlpha,
  226. } {
  227. format := format
  228. t.Run(fmt.Sprintf("format%d", format), func(t *testing.T) {
  229. dst := ebiten.NewImage(w, h)
  230. var cm colorm.ColorM
  231. cm.Translate(0.25, 0.25, 0.25, 0)
  232. op := &colorm.DrawTrianglesOptions{}
  233. op.ColorScaleMode = format
  234. colorm.DrawTriangles(dst, vs, is, src, cm, op)
  235. got := dst.At(0, 0).(color.RGBA)
  236. alphaBeforeScale := 0.5
  237. var want color.RGBA
  238. switch format {
  239. case ebiten.ColorScaleModeStraightAlpha:
  240. want = color.RGBA{
  241. R: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
  242. G: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25 * 0.75)),
  243. B: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5 * 0.75)),
  244. A: byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
  245. }
  246. case ebiten.ColorScaleModePremultipliedAlpha:
  247. want = color.RGBA{
  248. R: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
  249. G: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.25)),
  250. B: byte(math.Floor(0xff * (0.5/alphaBeforeScale + 0.25) * alphaBeforeScale * 0.5)),
  251. A: byte(math.Floor(0xff * alphaBeforeScale * 0.75)),
  252. }
  253. }
  254. if !sameColors(got, want, 2) {
  255. t.Errorf("got: %v, want: %v", got, want)
  256. }
  257. })
  258. }
  259. }
  260. // Issue #1213
  261. func TestColorMCopy(t *testing.T) {
  262. const w, h = 16, 16
  263. dst := ebiten.NewImage(w, h)
  264. src := ebiten.NewImage(w, h)
  265. for k := 0; k < 256; k++ {
  266. var cm colorm.ColorM
  267. cm.Translate(1, 1, 1, float64(k)/0xff)
  268. op := &colorm.DrawImageOptions{}
  269. op.Blend = ebiten.BlendCopy
  270. colorm.DrawImage(dst, src, cm, op)
  271. for j := 0; j < h; j++ {
  272. for i := 0; i < w; i++ {
  273. got := dst.At(i, j).(color.RGBA)
  274. want := color.RGBA{R: byte(k), G: byte(k), B: byte(k), A: byte(k)}
  275. if !sameColors(got, want, 1) {
  276. t.Fatalf("dst.At(%d, %d), k: %d: got %v, want %v", i, j, k, got, want)
  277. }
  278. }
  279. }
  280. }
  281. }