1
0

io_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. package imaging
  2. import (
  3. "bytes"
  4. "errors"
  5. "image"
  6. "image/color"
  7. "image/color/palette"
  8. "image/draw"
  9. "image/png"
  10. "io"
  11. "io/ioutil"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. "testing"
  16. )
  17. var (
  18. errCreate = errors.New("failed to create file")
  19. errClose = errors.New("failed to close file")
  20. errOpen = errors.New("failed to open file")
  21. )
  22. type badFS struct{}
  23. func (badFS) Create(name string) (io.WriteCloser, error) {
  24. if name == "badFile.jpg" {
  25. return badFile{ioutil.Discard}, nil
  26. }
  27. return nil, errCreate
  28. }
  29. func (badFS) Open(name string) (io.ReadCloser, error) {
  30. return nil, errOpen
  31. }
  32. type badFile struct {
  33. io.Writer
  34. }
  35. func (badFile) Close() error {
  36. return errClose
  37. }
  38. type quantizer struct {
  39. palette []color.Color
  40. }
  41. func (q quantizer) Quantize(p color.Palette, m image.Image) color.Palette {
  42. pal := make([]color.Color, len(p), cap(p))
  43. copy(pal, p)
  44. n := cap(p) - len(p)
  45. if n > len(q.palette) {
  46. n = len(q.palette)
  47. }
  48. for i := 0; i < n; i++ {
  49. pal = append(pal, q.palette[i])
  50. }
  51. return pal
  52. }
  53. func TestOpenSave(t *testing.T) {
  54. imgWithoutAlpha := image.NewNRGBA(image.Rect(0, 0, 4, 6))
  55. imgWithoutAlpha.Pix = []uint8{
  56. 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  57. 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  58. 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
  59. 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
  60. 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x88, 0x88, 0x88, 0xff, 0x88, 0x88, 0x88, 0xff,
  61. 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x88, 0x88, 0x88, 0xff, 0x88, 0x88, 0x88, 0xff,
  62. }
  63. imgWithAlpha := image.NewNRGBA(image.Rect(0, 0, 4, 6))
  64. imgWithAlpha.Pix = []uint8{
  65. 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  66. 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  67. 0xff, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0x00, 0xff, 0x00, 0x80, 0x00, 0xff, 0x00, 0x80,
  68. 0xff, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0x00, 0xff, 0x00, 0x80, 0x00, 0xff, 0x00, 0x80,
  69. 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x00,
  70. 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x00,
  71. }
  72. options := [][]EncodeOption{
  73. {
  74. JPEGQuality(100),
  75. },
  76. {
  77. JPEGQuality(99),
  78. GIFDrawer(draw.FloydSteinberg),
  79. GIFNumColors(256),
  80. GIFQuantizer(quantizer{palette.Plan9}),
  81. PNGCompressionLevel(png.BestSpeed),
  82. },
  83. }
  84. dir, err := ioutil.TempDir("", "imaging")
  85. if err != nil {
  86. t.Fatalf("failed to create temporary directory: %v", err)
  87. }
  88. defer os.RemoveAll(dir)
  89. for _, ext := range []string{"jpg", "jpeg", "png", "gif", "bmp", "tif", "tiff"} {
  90. filename := filepath.Join(dir, "test."+ext)
  91. img := imgWithoutAlpha
  92. if ext == "png" {
  93. img = imgWithAlpha
  94. }
  95. for _, opts := range options {
  96. err := Save(img, filename, opts...)
  97. if err != nil {
  98. t.Fatalf("failed to save image (%q): %v", filename, err)
  99. }
  100. img2, err := Open(filename)
  101. if err != nil {
  102. t.Fatalf("failed to open image (%q): %v", filename, err)
  103. }
  104. got := Clone(img2)
  105. delta := 0
  106. if ext == "jpg" || ext == "jpeg" || ext == "gif" {
  107. delta = 3
  108. }
  109. if !compareNRGBA(got, img, delta) {
  110. t.Fatalf("bad encode-decode result (ext=%q): got %#v want %#v", ext, got, img)
  111. }
  112. }
  113. }
  114. buf := &bytes.Buffer{}
  115. err = Encode(buf, imgWithAlpha, JPEG)
  116. if err != nil {
  117. t.Fatalf("failed to encode alpha to JPEG: %v", err)
  118. }
  119. buf = &bytes.Buffer{}
  120. err = Encode(buf, imgWithAlpha, Format(100))
  121. if err != ErrUnsupportedFormat {
  122. t.Fatalf("got %v want ErrUnsupportedFormat", err)
  123. }
  124. buf = bytes.NewBuffer([]byte("bad data"))
  125. _, err = Decode(buf)
  126. if err == nil {
  127. t.Fatalf("decoding bad data: expected error got nil")
  128. }
  129. err = Save(imgWithAlpha, filepath.Join(dir, "test.unknown"))
  130. if err != ErrUnsupportedFormat {
  131. t.Fatalf("got %v want ErrUnsupportedFormat", err)
  132. }
  133. prevFS := fs
  134. fs = badFS{}
  135. defer func() { fs = prevFS }()
  136. err = Save(imgWithAlpha, "test.jpg")
  137. if err != errCreate {
  138. t.Fatalf("got error %v want errCreate", err)
  139. }
  140. err = Save(imgWithAlpha, "badFile.jpg")
  141. if err != errClose {
  142. t.Fatalf("got error %v want errClose", err)
  143. }
  144. _, err = Open("test.jpg")
  145. if err != errOpen {
  146. t.Fatalf("got error %v want errOpen", err)
  147. }
  148. }
  149. func TestFormats(t *testing.T) {
  150. formatNames := map[Format]string{
  151. JPEG: "JPEG",
  152. PNG: "PNG",
  153. GIF: "GIF",
  154. BMP: "BMP",
  155. TIFF: "TIFF",
  156. Format(-1): "",
  157. }
  158. for format, name := range formatNames {
  159. got := format.String()
  160. if got != name {
  161. t.Fatalf("got format name %q want %q", got, name)
  162. }
  163. }
  164. }
  165. func TestFormatFromExtension(t *testing.T) {
  166. testCases := []struct {
  167. name string
  168. ext string
  169. want Format
  170. err error
  171. }{
  172. {
  173. name: "jpg without leading dot",
  174. ext: "jpg",
  175. want: JPEG,
  176. },
  177. {
  178. name: "jpg with leading dot",
  179. ext: ".jpg",
  180. want: JPEG,
  181. },
  182. {
  183. name: "jpg uppercase",
  184. ext: ".JPG",
  185. want: JPEG,
  186. },
  187. {
  188. name: "unsupported",
  189. ext: ".unsupportedextension",
  190. want: -1,
  191. err: ErrUnsupportedFormat,
  192. },
  193. }
  194. for _, tc := range testCases {
  195. t.Run(tc.name, func(t *testing.T) {
  196. got, err := FormatFromExtension(tc.ext)
  197. if err != tc.err {
  198. t.Errorf("got error %#v want %#v", err, tc.err)
  199. }
  200. if got != tc.want {
  201. t.Errorf("got result %#v want %#v", got, tc.want)
  202. }
  203. })
  204. }
  205. }
  206. func TestReadOrientation(t *testing.T) {
  207. testCases := []struct {
  208. path string
  209. orient orientation
  210. }{
  211. {"testdata/orientation_0.jpg", 0},
  212. {"testdata/orientation_1.jpg", 1},
  213. {"testdata/orientation_2.jpg", 2},
  214. {"testdata/orientation_3.jpg", 3},
  215. {"testdata/orientation_4.jpg", 4},
  216. {"testdata/orientation_5.jpg", 5},
  217. {"testdata/orientation_6.jpg", 6},
  218. {"testdata/orientation_7.jpg", 7},
  219. {"testdata/orientation_8.jpg", 8},
  220. }
  221. for _, tc := range testCases {
  222. f, err := os.Open(tc.path)
  223. if err != nil {
  224. t.Fatalf("%q: failed to open: %v", tc.path, err)
  225. }
  226. orient := readOrientation(f)
  227. if orient != tc.orient {
  228. t.Fatalf("%q: got orientation %d want %d", tc.path, orient, tc.orient)
  229. }
  230. }
  231. }
  232. func TestReadOrientationFails(t *testing.T) {
  233. testCases := []struct {
  234. name string
  235. data string
  236. }{
  237. {
  238. "empty",
  239. "",
  240. },
  241. {
  242. "missing SOI marker",
  243. "\xff\xe1",
  244. },
  245. {
  246. "missing APP1 marker",
  247. "\xff\xd8",
  248. },
  249. {
  250. "short read marker",
  251. "\xff\xd8\xff",
  252. },
  253. {
  254. "short read block size",
  255. "\xff\xd8\xff\xe1\x00",
  256. },
  257. {
  258. "invalid marker",
  259. "\xff\xd8\x00\xe1\x00\x00",
  260. },
  261. {
  262. "block size too small",
  263. "\xff\xd8\xff\xe0\x00\x01",
  264. },
  265. {
  266. "short read block",
  267. "\xff\xd8\xff\xe0\x00\x08\x00",
  268. },
  269. {
  270. "missing EXIF header",
  271. "\xff\xd8\xff\xe1\x00\xff",
  272. },
  273. {
  274. "invalid EXIF header",
  275. "\xff\xd8\xff\xe1\x00\xff\x00\x00\x00\x00",
  276. },
  277. {
  278. "missing EXIF header tail",
  279. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66",
  280. },
  281. {
  282. "missing byte order tag",
  283. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00",
  284. },
  285. {
  286. "invalid byte order tag",
  287. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x00\x00",
  288. },
  289. {
  290. "missing byte order tail",
  291. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x49\x49",
  292. },
  293. {
  294. "missing exif offset",
  295. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x49\x49\x00\x2a",
  296. },
  297. {
  298. "invalid exif offset",
  299. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x07",
  300. },
  301. {
  302. "read exif offset error",
  303. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x09",
  304. },
  305. {
  306. "missing number of tags",
  307. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08",
  308. },
  309. {
  310. "zero number of tags",
  311. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x00",
  312. },
  313. {
  314. "missing tag",
  315. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01",
  316. },
  317. {
  318. "missing tag offset",
  319. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01\x00\x00",
  320. },
  321. {
  322. "missing orientation tag",
  323. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  324. },
  325. {
  326. "missing orientation tag value offset",
  327. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01\x01\x12",
  328. },
  329. {
  330. "missing orientation value",
  331. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01\x01\x12\x00\x03\x00\x00\x00\x01",
  332. },
  333. {
  334. "invalid orientation value",
  335. "\xff\xd8\xff\xe1\x00\xff\x45\x78\x69\x66\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08\x00\x01\x01\x12\x00\x03\x00\x00\x00\x01\x00\x09",
  336. },
  337. }
  338. for _, tc := range testCases {
  339. t.Run(tc.name, func(t *testing.T) {
  340. if o := readOrientation(strings.NewReader(tc.data)); o != orientationUnspecified {
  341. t.Fatalf("got orientation %d want %d", o, orientationUnspecified)
  342. }
  343. })
  344. }
  345. }
  346. func TestAutoOrientation(t *testing.T) {
  347. toBW := func(img image.Image) []byte {
  348. b := img.Bounds()
  349. data := make([]byte, 0, b.Dx()*b.Dy())
  350. for x := b.Min.X; x < b.Max.X; x++ {
  351. for y := b.Min.Y; y < b.Max.Y; y++ {
  352. c := color.GrayModel.Convert(img.At(x, y)).(color.Gray)
  353. if c.Y < 128 {
  354. data = append(data, 1)
  355. } else {
  356. data = append(data, 0)
  357. }
  358. }
  359. }
  360. return data
  361. }
  362. f, err := os.Open("testdata/orientation_0.jpg")
  363. if err != nil {
  364. t.Fatalf("os.Open(%q): %v", "testdata/orientation_0.jpg", err)
  365. }
  366. orig, _, err := image.Decode(f)
  367. if err != nil {
  368. t.Fatalf("image.Decode(%q): %v", "testdata/orientation_0.jpg", err)
  369. }
  370. origBW := toBW(orig)
  371. testCases := []struct {
  372. path string
  373. }{
  374. {"testdata/orientation_0.jpg"},
  375. {"testdata/orientation_1.jpg"},
  376. {"testdata/orientation_2.jpg"},
  377. {"testdata/orientation_3.jpg"},
  378. {"testdata/orientation_4.jpg"},
  379. {"testdata/orientation_5.jpg"},
  380. {"testdata/orientation_6.jpg"},
  381. {"testdata/orientation_7.jpg"},
  382. {"testdata/orientation_8.jpg"},
  383. }
  384. for _, tc := range testCases {
  385. img, err := Open(tc.path, AutoOrientation(true))
  386. if err != nil {
  387. t.Fatal(err)
  388. }
  389. if img.Bounds() != orig.Bounds() {
  390. t.Fatalf("%s: got bounds %v want %v", tc.path, img.Bounds(), orig.Bounds())
  391. }
  392. imgBW := toBW(img)
  393. if !bytes.Equal(imgBW, origBW) {
  394. t.Fatalf("%s: got bw data %v want %v", tc.path, imgBW, origBW)
  395. }
  396. }
  397. if _, err := Decode(strings.NewReader("invalid data"), AutoOrientation(true)); err == nil {
  398. t.Fatal("expected error got nil")
  399. }
  400. }