git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/imaging/scanner_test.go (about) 1 package imaging 2 3 import ( 4 "fmt" 5 "image" 6 "image/color" 7 "image/color/palette" 8 "image/draw" 9 "testing" 10 ) 11 12 func TestScanner(t *testing.T) { 13 rect := image.Rect(-1, -1, 15, 15) 14 colors := palette.Plan9 15 testCases := []struct { 16 name string 17 img image.Image 18 }{ 19 { 20 name: "NRGBA", 21 img: makeNRGBAImage(rect, colors), 22 }, 23 { 24 name: "NRGBA64", 25 img: makeNRGBA64Image(rect, colors), 26 }, 27 { 28 name: "RGBA", 29 img: makeRGBAImage(rect, colors), 30 }, 31 { 32 name: "RGBA64", 33 img: makeRGBA64Image(rect, colors), 34 }, 35 { 36 name: "Gray", 37 img: makeGrayImage(rect, colors), 38 }, 39 { 40 name: "Gray16", 41 img: makeGray16Image(rect, colors), 42 }, 43 { 44 name: "YCbCr-444", 45 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio444), 46 }, 47 { 48 name: "YCbCr-422", 49 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio422), 50 }, 51 { 52 name: "YCbCr-420", 53 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio420), 54 }, 55 { 56 name: "YCbCr-440", 57 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio440), 58 }, 59 { 60 name: "YCbCr-410", 61 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio410), 62 }, 63 { 64 name: "YCbCr-411", 65 img: makeYCbCrImage(rect, colors, image.YCbCrSubsampleRatio411), 66 }, 67 { 68 name: "Paletted", 69 img: makePalettedImage(rect, colors), 70 }, 71 { 72 name: "Alpha", 73 img: makeAlphaImage(rect, colors), 74 }, 75 { 76 name: "Alpha16", 77 img: makeAlpha16Image(rect, colors), 78 }, 79 { 80 name: "Generic", 81 img: makeGenericImage(rect, colors), 82 }, 83 } 84 85 for _, tc := range testCases { 86 t.Run(tc.name, func(t *testing.T) { 87 r := tc.img.Bounds() 88 s := newScanner(tc.img) 89 for y := r.Min.Y; y < r.Max.Y; y++ { 90 buf := make([]byte, r.Dx()*4) 91 s.scan(0, y-r.Min.Y, r.Dx(), y+1-r.Min.Y, buf) 92 wantBuf := readRow(tc.img, y) 93 if !compareBytes(buf, wantBuf, 1) { 94 fmt.Println(tc.img) 95 t.Fatalf("scan horizontal line (y=%d): got %v want %v", y, buf, wantBuf) 96 } 97 } 98 for x := r.Min.X; x < r.Max.X; x++ { 99 buf := make([]byte, r.Dy()*4) 100 s.scan(x-r.Min.X, 0, x+1-r.Min.X, r.Dy(), buf) 101 wantBuf := readColumn(tc.img, x) 102 if !compareBytes(buf, wantBuf, 1) { 103 t.Fatalf("scan vertical line (x=%d): got %v want %v", x, buf, wantBuf) 104 } 105 } 106 }) 107 } 108 } 109 110 func makeYCbCrImage(rect image.Rectangle, colors []color.Color, sr image.YCbCrSubsampleRatio) *image.YCbCr { 111 img := image.NewYCbCr(rect, sr) 112 j := 0 113 for y := rect.Min.Y; y < rect.Max.Y; y++ { 114 for x := rect.Min.X; x < rect.Max.X; x++ { 115 iy := img.YOffset(x, y) 116 ic := img.COffset(x, y) 117 c := color.NRGBAModel.Convert(colors[j]).(color.NRGBA) 118 img.Y[iy], img.Cb[ic], img.Cr[ic] = color.RGBToYCbCr(c.R, c.G, c.B) 119 j++ 120 } 121 } 122 return img 123 } 124 125 func makeNRGBAImage(rect image.Rectangle, colors []color.Color) *image.NRGBA { 126 img := image.NewNRGBA(rect) 127 fillDrawImage(img, colors) 128 return img 129 } 130 131 func makeNRGBA64Image(rect image.Rectangle, colors []color.Color) *image.NRGBA64 { 132 img := image.NewNRGBA64(rect) 133 fillDrawImage(img, colors) 134 return img 135 } 136 137 func makeRGBAImage(rect image.Rectangle, colors []color.Color) *image.RGBA { 138 img := image.NewRGBA(rect) 139 fillDrawImage(img, colors) 140 return img 141 } 142 143 func makeRGBA64Image(rect image.Rectangle, colors []color.Color) *image.RGBA64 { 144 img := image.NewRGBA64(rect) 145 fillDrawImage(img, colors) 146 return img 147 } 148 149 func makeGrayImage(rect image.Rectangle, colors []color.Color) *image.Gray { 150 img := image.NewGray(rect) 151 fillDrawImage(img, colors) 152 return img 153 } 154 155 func makeGray16Image(rect image.Rectangle, colors []color.Color) *image.Gray16 { 156 img := image.NewGray16(rect) 157 fillDrawImage(img, colors) 158 return img 159 } 160 161 func makePalettedImage(rect image.Rectangle, colors []color.Color) *image.Paletted { 162 img := image.NewPaletted(rect, colors) 163 fillDrawImage(img, colors) 164 return img 165 } 166 167 func makeAlphaImage(rect image.Rectangle, colors []color.Color) *image.Alpha { 168 img := image.NewAlpha(rect) 169 fillDrawImage(img, colors) 170 return img 171 } 172 173 func makeAlpha16Image(rect image.Rectangle, colors []color.Color) *image.Alpha16 { 174 img := image.NewAlpha16(rect) 175 fillDrawImage(img, colors) 176 return img 177 } 178 179 func makeGenericImage(rect image.Rectangle, colors []color.Color) image.Image { 180 img := image.NewRGBA(rect) 181 fillDrawImage(img, colors) 182 type genericImage struct{ *image.RGBA } 183 return &genericImage{img} 184 } 185 186 func fillDrawImage(img draw.Image, colors []color.Color) { 187 colorsNRGBA := make([]color.NRGBA, len(colors)) 188 for i, c := range colors { 189 nrgba := color.NRGBAModel.Convert(c).(color.NRGBA) 190 nrgba.A = uint8(i % 256) 191 colorsNRGBA[i] = nrgba 192 } 193 rect := img.Bounds() 194 i := 0 195 for y := rect.Min.Y; y < rect.Max.Y; y++ { 196 for x := rect.Min.X; x < rect.Max.X; x++ { 197 img.Set(x, y, colorsNRGBA[i]) 198 i++ 199 } 200 } 201 } 202 203 func readRow(img image.Image, y int) []uint8 { 204 row := make([]byte, img.Bounds().Dx()*4) 205 i := 0 206 for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { 207 c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) 208 row[i+0] = c.R 209 row[i+1] = c.G 210 row[i+2] = c.B 211 row[i+3] = c.A 212 i += 4 213 } 214 return row 215 } 216 217 func readColumn(img image.Image, x int) []uint8 { 218 column := make([]byte, img.Bounds().Dy()*4) 219 i := 0 220 for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { 221 c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) 222 column[i+0] = c.R 223 column[i+1] = c.G 224 column[i+2] = c.B 225 column[i+3] = c.A 226 i += 4 227 } 228 return column 229 }