PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/gofrontend/libgo/go/image/draw/draw_test.go

http://github.com/axw/llgo
Go | 469 lines | 386 code | 26 blank | 57 comment | 73 complexity | 22792c134b4ad5ba6ae666c581fdeda6 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package draw
  5. import (
  6. "image"
  7. "image/color"
  8. "image/png"
  9. "os"
  10. "testing"
  11. )
  12. func eq(c0, c1 color.Color) bool {
  13. r0, g0, b0, a0 := c0.RGBA()
  14. r1, g1, b1, a1 := c1.RGBA()
  15. return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
  16. }
  17. func fillBlue(alpha int) image.Image {
  18. return image.NewUniform(color.RGBA{0, 0, uint8(alpha), uint8(alpha)})
  19. }
  20. func fillAlpha(alpha int) image.Image {
  21. return image.NewUniform(color.Alpha{uint8(alpha)})
  22. }
  23. func vgradGreen(alpha int) image.Image {
  24. m := image.NewRGBA(image.Rect(0, 0, 16, 16))
  25. for y := 0; y < 16; y++ {
  26. for x := 0; x < 16; x++ {
  27. m.Set(x, y, color.RGBA{0, uint8(y * alpha / 15), 0, uint8(alpha)})
  28. }
  29. }
  30. return m
  31. }
  32. func vgradAlpha(alpha int) image.Image {
  33. m := image.NewAlpha(image.Rect(0, 0, 16, 16))
  34. for y := 0; y < 16; y++ {
  35. for x := 0; x < 16; x++ {
  36. m.Set(x, y, color.Alpha{uint8(y * alpha / 15)})
  37. }
  38. }
  39. return m
  40. }
  41. func vgradGreenNRGBA(alpha int) image.Image {
  42. m := image.NewNRGBA(image.Rect(0, 0, 16, 16))
  43. for y := 0; y < 16; y++ {
  44. for x := 0; x < 16; x++ {
  45. m.Set(x, y, color.RGBA{0, uint8(y * 0x11), 0, uint8(alpha)})
  46. }
  47. }
  48. return m
  49. }
  50. func vgradCr() image.Image {
  51. m := &image.YCbCr{
  52. Y: make([]byte, 16*16),
  53. Cb: make([]byte, 16*16),
  54. Cr: make([]byte, 16*16),
  55. YStride: 16,
  56. CStride: 16,
  57. SubsampleRatio: image.YCbCrSubsampleRatio444,
  58. Rect: image.Rect(0, 0, 16, 16),
  59. }
  60. for y := 0; y < 16; y++ {
  61. for x := 0; x < 16; x++ {
  62. m.Cr[y*m.CStride+x] = uint8(y * 0x11)
  63. }
  64. }
  65. return m
  66. }
  67. func vgradGray() image.Image {
  68. m := image.NewGray(image.Rect(0, 0, 16, 16))
  69. for y := 0; y < 16; y++ {
  70. for x := 0; x < 16; x++ {
  71. m.Set(x, y, color.Gray{uint8(y * 0x11)})
  72. }
  73. }
  74. return m
  75. }
  76. func vgradMagenta() image.Image {
  77. m := image.NewCMYK(image.Rect(0, 0, 16, 16))
  78. for y := 0; y < 16; y++ {
  79. for x := 0; x < 16; x++ {
  80. m.Set(x, y, color.CMYK{0, uint8(y * 0x11), 0, 0x3f})
  81. }
  82. }
  83. return m
  84. }
  85. func hgradRed(alpha int) Image {
  86. m := image.NewRGBA(image.Rect(0, 0, 16, 16))
  87. for y := 0; y < 16; y++ {
  88. for x := 0; x < 16; x++ {
  89. m.Set(x, y, color.RGBA{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
  90. }
  91. }
  92. return m
  93. }
  94. func gradYellow(alpha int) Image {
  95. m := image.NewRGBA(image.Rect(0, 0, 16, 16))
  96. for y := 0; y < 16; y++ {
  97. for x := 0; x < 16; x++ {
  98. m.Set(x, y, color.RGBA{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)})
  99. }
  100. }
  101. return m
  102. }
  103. type drawTest struct {
  104. desc string
  105. src image.Image
  106. mask image.Image
  107. op Op
  108. expected color.Color
  109. }
  110. var drawTests = []drawTest{
  111. // Uniform mask (0% opaque).
  112. {"nop", vgradGreen(255), fillAlpha(0), Over, color.RGBA{136, 0, 0, 255}},
  113. {"clear", vgradGreen(255), fillAlpha(0), Src, color.RGBA{0, 0, 0, 0}},
  114. // Uniform mask (100%, 75%, nil) and uniform source.
  115. // At (x, y) == (8, 8):
  116. // The destination pixel is {136, 0, 0, 255}.
  117. // The source pixel is {0, 0, 90, 90}.
  118. {"fill", fillBlue(90), fillAlpha(255), Over, color.RGBA{88, 0, 90, 255}},
  119. {"fillSrc", fillBlue(90), fillAlpha(255), Src, color.RGBA{0, 0, 90, 90}},
  120. {"fillAlpha", fillBlue(90), fillAlpha(192), Over, color.RGBA{100, 0, 68, 255}},
  121. {"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, color.RGBA{0, 0, 68, 68}},
  122. {"fillNil", fillBlue(90), nil, Over, color.RGBA{88, 0, 90, 255}},
  123. {"fillNilSrc", fillBlue(90), nil, Src, color.RGBA{0, 0, 90, 90}},
  124. // Uniform mask (100%, 75%, nil) and variable source.
  125. // At (x, y) == (8, 8):
  126. // The destination pixel is {136, 0, 0, 255}.
  127. // The source pixel is {0, 48, 0, 90}.
  128. {"copy", vgradGreen(90), fillAlpha(255), Over, color.RGBA{88, 48, 0, 255}},
  129. {"copySrc", vgradGreen(90), fillAlpha(255), Src, color.RGBA{0, 48, 0, 90}},
  130. {"copyAlpha", vgradGreen(90), fillAlpha(192), Over, color.RGBA{100, 36, 0, 255}},
  131. {"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, color.RGBA{0, 36, 0, 68}},
  132. {"copyNil", vgradGreen(90), nil, Over, color.RGBA{88, 48, 0, 255}},
  133. {"copyNilSrc", vgradGreen(90), nil, Src, color.RGBA{0, 48, 0, 90}},
  134. // Uniform mask (100%, 75%, nil) and variable NRGBA source.
  135. // At (x, y) == (8, 8):
  136. // The destination pixel is {136, 0, 0, 255}.
  137. // The source pixel is {0, 136, 0, 90} in NRGBA-space, which is {0, 48, 0, 90} in RGBA-space.
  138. // The result pixel is different than in the "copy*" test cases because of rounding errors.
  139. {"nrgba", vgradGreenNRGBA(90), fillAlpha(255), Over, color.RGBA{88, 46, 0, 255}},
  140. {"nrgbaSrc", vgradGreenNRGBA(90), fillAlpha(255), Src, color.RGBA{0, 46, 0, 90}},
  141. {"nrgbaAlpha", vgradGreenNRGBA(90), fillAlpha(192), Over, color.RGBA{100, 34, 0, 255}},
  142. {"nrgbaAlphaSrc", vgradGreenNRGBA(90), fillAlpha(192), Src, color.RGBA{0, 34, 0, 68}},
  143. {"nrgbaNil", vgradGreenNRGBA(90), nil, Over, color.RGBA{88, 46, 0, 255}},
  144. {"nrgbaNilSrc", vgradGreenNRGBA(90), nil, Src, color.RGBA{0, 46, 0, 90}},
  145. // Uniform mask (100%, 75%, nil) and variable YCbCr source.
  146. // At (x, y) == (8, 8):
  147. // The destination pixel is {136, 0, 0, 255}.
  148. // The source pixel is {0, 0, 136} in YCbCr-space, which is {11, 38, 0, 255} in RGB-space.
  149. {"ycbcr", vgradCr(), fillAlpha(255), Over, color.RGBA{11, 38, 0, 255}},
  150. {"ycbcrSrc", vgradCr(), fillAlpha(255), Src, color.RGBA{11, 38, 0, 255}},
  151. {"ycbcrAlpha", vgradCr(), fillAlpha(192), Over, color.RGBA{42, 28, 0, 255}},
  152. {"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, color.RGBA{8, 28, 0, 192}},
  153. {"ycbcrNil", vgradCr(), nil, Over, color.RGBA{11, 38, 0, 255}},
  154. {"ycbcrNilSrc", vgradCr(), nil, Src, color.RGBA{11, 38, 0, 255}},
  155. // Uniform mask (100%, 75%, nil) and variable Gray source.
  156. // At (x, y) == (8, 8):
  157. // The destination pixel is {136, 0, 0, 255}.
  158. // The source pixel is {136} in Gray-space, which is {136, 136, 136, 255} in RGBA-space.
  159. {"gray", vgradGray(), fillAlpha(255), Over, color.RGBA{136, 136, 136, 255}},
  160. {"graySrc", vgradGray(), fillAlpha(255), Src, color.RGBA{136, 136, 136, 255}},
  161. {"grayAlpha", vgradGray(), fillAlpha(192), Over, color.RGBA{136, 102, 102, 255}},
  162. {"grayAlphaSrc", vgradGray(), fillAlpha(192), Src, color.RGBA{102, 102, 102, 192}},
  163. {"grayNil", vgradGray(), nil, Over, color.RGBA{136, 136, 136, 255}},
  164. {"grayNilSrc", vgradGray(), nil, Src, color.RGBA{136, 136, 136, 255}},
  165. // Uniform mask (100%, 75%, nil) and variable CMYK source.
  166. // At (x, y) == (8, 8):
  167. // The destination pixel is {136, 0, 0, 255}.
  168. // The source pixel is {0, 136, 0, 63} in CMYK-space, which is {192, 89, 192} in RGB-space.
  169. {"cmyk", vgradMagenta(), fillAlpha(255), Over, color.RGBA{192, 89, 192, 255}},
  170. {"cmykSrc", vgradMagenta(), fillAlpha(255), Src, color.RGBA{192, 89, 192, 255}},
  171. {"cmykAlpha", vgradMagenta(), fillAlpha(192), Over, color.RGBA{178, 67, 145, 255}},
  172. {"cmykAlphaSrc", vgradMagenta(), fillAlpha(192), Src, color.RGBA{145, 67, 145, 192}},
  173. {"cmykNil", vgradMagenta(), nil, Over, color.RGBA{192, 89, 192, 255}},
  174. {"cmykNilSrc", vgradMagenta(), nil, Src, color.RGBA{192, 89, 192, 255}},
  175. // Variable mask and variable source.
  176. // At (x, y) == (8, 8):
  177. // The destination pixel is {136, 0, 0, 255}.
  178. // The source pixel is {0, 0, 255, 255}.
  179. // The mask pixel's alpha is 102, or 40%.
  180. {"generic", fillBlue(255), vgradAlpha(192), Over, color.RGBA{81, 0, 102, 255}},
  181. {"genericSrc", fillBlue(255), vgradAlpha(192), Src, color.RGBA{0, 0, 102, 102}},
  182. }
  183. func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
  184. // Since golden is a newly allocated image, we don't have to check if the
  185. // input source and mask images and the output golden image overlap.
  186. b := dst.Bounds()
  187. sb := src.Bounds()
  188. mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
  189. if mask != nil {
  190. mb = mask.Bounds()
  191. }
  192. golden := image.NewRGBA(image.Rect(0, 0, b.Max.X, b.Max.Y))
  193. for y := r.Min.Y; y < r.Max.Y; y++ {
  194. sy := y + sp.Y - r.Min.Y
  195. my := y + mp.Y - r.Min.Y
  196. for x := r.Min.X; x < r.Max.X; x++ {
  197. if !(image.Pt(x, y).In(b)) {
  198. continue
  199. }
  200. sx := x + sp.X - r.Min.X
  201. if !(image.Pt(sx, sy).In(sb)) {
  202. continue
  203. }
  204. mx := x + mp.X - r.Min.X
  205. if !(image.Pt(mx, my).In(mb)) {
  206. continue
  207. }
  208. const M = 1<<16 - 1
  209. var dr, dg, db, da uint32
  210. if op == Over {
  211. dr, dg, db, da = dst.At(x, y).RGBA()
  212. }
  213. sr, sg, sb, sa := src.At(sx, sy).RGBA()
  214. ma := uint32(M)
  215. if mask != nil {
  216. _, _, _, ma = mask.At(mx, my).RGBA()
  217. }
  218. a := M - (sa * ma / M)
  219. golden.Set(x, y, color.RGBA64{
  220. uint16((dr*a + sr*ma) / M),
  221. uint16((dg*a + sg*ma) / M),
  222. uint16((db*a + sb*ma) / M),
  223. uint16((da*a + sa*ma) / M),
  224. })
  225. }
  226. }
  227. return golden.SubImage(b)
  228. }
  229. func TestDraw(t *testing.T) {
  230. rr := []image.Rectangle{
  231. image.Rect(0, 0, 0, 0),
  232. image.Rect(0, 0, 16, 16),
  233. image.Rect(3, 5, 12, 10),
  234. image.Rect(0, 0, 9, 9),
  235. image.Rect(8, 8, 16, 16),
  236. image.Rect(8, 0, 9, 16),
  237. image.Rect(0, 8, 16, 9),
  238. image.Rect(8, 8, 9, 9),
  239. image.Rect(8, 8, 8, 8),
  240. }
  241. for _, r := range rr {
  242. loop:
  243. for _, test := range drawTests {
  244. dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
  245. // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
  246. golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
  247. b := dst.Bounds()
  248. if !b.Eq(golden.Bounds()) {
  249. t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
  250. continue
  251. }
  252. // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
  253. DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
  254. if image.Pt(8, 8).In(r) {
  255. // Check that the resultant pixel at (8, 8) matches what we expect
  256. // (the expected value can be verified by hand).
  257. if !eq(dst.At(8, 8), test.expected) {
  258. t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
  259. continue
  260. }
  261. }
  262. // Check that the resultant dst image matches the golden output.
  263. for y := b.Min.Y; y < b.Max.Y; y++ {
  264. for x := b.Min.X; x < b.Max.X; x++ {
  265. if !eq(dst.At(x, y), golden.At(x, y)) {
  266. t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
  267. continue loop
  268. }
  269. }
  270. }
  271. }
  272. }
  273. }
  274. func TestDrawOverlap(t *testing.T) {
  275. for _, op := range []Op{Over, Src} {
  276. for yoff := -2; yoff <= 2; yoff++ {
  277. loop:
  278. for xoff := -2; xoff <= 2; xoff++ {
  279. m := gradYellow(127).(*image.RGBA)
  280. dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
  281. src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
  282. b := dst.Bounds()
  283. // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
  284. golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
  285. if !b.Eq(golden.Bounds()) {
  286. t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
  287. continue
  288. }
  289. // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
  290. DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
  291. // Check that the resultant dst image matches the golden output.
  292. for y := b.Min.Y; y < b.Max.Y; y++ {
  293. for x := b.Min.X; x < b.Max.X; x++ {
  294. if !eq(dst.At(x, y), golden.At(x, y)) {
  295. t.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff, yoff, x, y, dst.At(x, y), golden.At(x, y))
  296. continue loop
  297. }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. }
  304. // TestNonZeroSrcPt checks drawing with a non-zero src point parameter.
  305. func TestNonZeroSrcPt(t *testing.T) {
  306. a := image.NewRGBA(image.Rect(0, 0, 1, 1))
  307. b := image.NewRGBA(image.Rect(0, 0, 2, 2))
  308. b.Set(0, 0, color.RGBA{0, 0, 0, 5})
  309. b.Set(1, 0, color.RGBA{0, 0, 5, 5})
  310. b.Set(0, 1, color.RGBA{0, 5, 0, 5})
  311. b.Set(1, 1, color.RGBA{5, 0, 0, 5})
  312. Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over)
  313. if !eq(color.RGBA{5, 0, 0, 5}, a.At(0, 0)) {
  314. t.Errorf("non-zero src pt: want %v got %v", color.RGBA{5, 0, 0, 5}, a.At(0, 0))
  315. }
  316. }
  317. func TestFill(t *testing.T) {
  318. rr := []image.Rectangle{
  319. image.Rect(0, 0, 0, 0),
  320. image.Rect(0, 0, 40, 30),
  321. image.Rect(10, 0, 40, 30),
  322. image.Rect(0, 20, 40, 30),
  323. image.Rect(10, 20, 40, 30),
  324. image.Rect(10, 20, 15, 25),
  325. image.Rect(10, 0, 35, 30),
  326. image.Rect(0, 15, 40, 16),
  327. image.Rect(24, 24, 25, 25),
  328. image.Rect(23, 23, 26, 26),
  329. image.Rect(22, 22, 27, 27),
  330. image.Rect(21, 21, 28, 28),
  331. image.Rect(20, 20, 29, 29),
  332. }
  333. for _, r := range rr {
  334. m := image.NewRGBA(image.Rect(0, 0, 40, 30)).SubImage(r).(*image.RGBA)
  335. b := m.Bounds()
  336. c := color.RGBA{11, 0, 0, 255}
  337. src := &image.Uniform{C: c}
  338. check := func(desc string) {
  339. for y := b.Min.Y; y < b.Max.Y; y++ {
  340. for x := b.Min.X; x < b.Max.X; x++ {
  341. if !eq(c, m.At(x, y)) {
  342. t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
  343. return
  344. }
  345. }
  346. }
  347. }
  348. // Draw 1 pixel at a time.
  349. for y := b.Min.Y; y < b.Max.Y; y++ {
  350. for x := b.Min.X; x < b.Max.X; x++ {
  351. DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
  352. }
  353. }
  354. check("pixel")
  355. // Draw 1 row at a time.
  356. c = color.RGBA{0, 22, 0, 255}
  357. src = &image.Uniform{C: c}
  358. for y := b.Min.Y; y < b.Max.Y; y++ {
  359. DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
  360. }
  361. check("row")
  362. // Draw 1 column at a time.
  363. c = color.RGBA{0, 0, 33, 255}
  364. src = &image.Uniform{C: c}
  365. for x := b.Min.X; x < b.Max.X; x++ {
  366. DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
  367. }
  368. check("column")
  369. // Draw the whole image at once.
  370. c = color.RGBA{44, 55, 66, 77}
  371. src = &image.Uniform{C: c}
  372. DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
  373. check("whole")
  374. }
  375. }
  376. // TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg
  377. // error diffusion of a uniform 50% gray source image with a black-and-white
  378. // palette is a checkerboard pattern.
  379. func TestFloydSteinbergCheckerboard(t *testing.T) {
  380. b := image.Rect(0, 0, 640, 480)
  381. // We can't represent 50% exactly, but 0x7fff / 0xffff is close enough.
  382. src := &image.Uniform{color.Gray16{0x7fff}}
  383. dst := image.NewPaletted(b, color.Palette{color.Black, color.White})
  384. FloydSteinberg.Draw(dst, b, src, image.Point{})
  385. nErr := 0
  386. for y := b.Min.Y; y < b.Max.Y; y++ {
  387. for x := b.Min.X; x < b.Max.X; x++ {
  388. got := dst.Pix[dst.PixOffset(x, y)]
  389. want := uint8(x+y) % 2
  390. if got != want {
  391. t.Errorf("at (%d, %d): got %d, want %d", x, y, got, want)
  392. if nErr++; nErr == 10 {
  393. t.Fatal("there may be more errors")
  394. }
  395. }
  396. }
  397. }
  398. }
  399. // embeddedPaletted is an Image that behaves like an *image.Paletted but whose
  400. // type is not *image.Paletted.
  401. type embeddedPaletted struct {
  402. *image.Paletted
  403. }
  404. // TestPaletted tests that the drawPaletted function behaves the same
  405. // regardless of whether dst is an *image.Paletted.
  406. func TestPaletted(t *testing.T) {
  407. f, err := os.Open("../testdata/video-001.png")
  408. if err != nil {
  409. t.Fatalf("open: %v", err)
  410. }
  411. defer f.Close()
  412. src, err := png.Decode(f)
  413. if err != nil {
  414. t.Fatalf("decode: %v", err)
  415. }
  416. b := src.Bounds()
  417. cgaPalette := color.Palette{
  418. color.RGBA{0x00, 0x00, 0x00, 0xff},
  419. color.RGBA{0x55, 0xff, 0xff, 0xff},
  420. color.RGBA{0xff, 0x55, 0xff, 0xff},
  421. color.RGBA{0xff, 0xff, 0xff, 0xff},
  422. }
  423. drawers := map[string]Drawer{
  424. "src": Src,
  425. "floyd-steinberg": FloydSteinberg,
  426. }
  427. loop:
  428. for dName, d := range drawers {
  429. dst0 := image.NewPaletted(b, cgaPalette)
  430. dst1 := image.NewPaletted(b, cgaPalette)
  431. d.Draw(dst0, b, src, image.Point{})
  432. d.Draw(embeddedPaletted{dst1}, b, src, image.Point{})
  433. for y := b.Min.Y; y < b.Max.Y; y++ {
  434. for x := b.Min.X; x < b.Max.X; x++ {
  435. if !eq(dst0.At(x, y), dst1.At(x, y)) {
  436. t.Errorf("%s: at (%d, %d), %v versus %v",
  437. dName, x, y, dst0.At(x, y), dst1.At(x, y))
  438. continue loop
  439. }
  440. }
  441. }
  442. }
  443. }