icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

argb_test.go (2829B)


      1 package icns
      2 
      3 import (
      4 	"bytes"
      5 	"errors"
      6 	"image"
      7 	"image/color"
      8 	"testing"
      9 )
     10 
     11 // argbElement builds an ARGB payload for a gradient, returning it and the
     12 // image it describes.
     13 func argbElement(side int) (payload []byte, want *image.NRGBA) {
     14 	pixels := side * side
     15 	planes := make([]byte, pixels*4)
     16 	want = image.NewNRGBA(image.Rect(0, 0, side, side))
     17 	for y := 0; y < side; y++ {
     18 		for x := 0; x < side; x++ {
     19 			i := y*side + x
     20 			c := color.NRGBA{
     21 				R: uint8(x * 255 / side),
     22 				G: uint8(y * 255 / side),
     23 				B: 0x80,
     24 				A: uint8(255 - x*128/side),
     25 			}
     26 			// Alpha leads, then the colour channels.
     27 			planes[i] = c.A
     28 			planes[pixels+i] = c.R
     29 			planes[pixels*2+i] = c.G
     30 			planes[pixels*3+i] = c.B
     31 			want.SetNRGBA(x, y, c)
     32 		}
     33 	}
     34 	return append([]byte("ARGB"), rleLiterals(planes)...), want
     35 }
     36 
     37 func TestDecodeARGB(t *testing.T) {
     38 	t.Parallel()
     39 	const side = 16
     40 	payload, want := argbElement(side)
     41 
     42 	t.Run("sidebar icon", func(st *testing.T) {
     43 		data := file(encodeElement("ic04", payload))
     44 		desc, err := Probe(bytes.NewReader(data))
     45 		if err != nil {
     46 			st.Fatal(err)
     47 		}
     48 		if len(desc) != 1 || desc[0].ImageFormat != ImageFormatARGB {
     49 			st.Fatalf("Probe = %v, want one ARGB icon", desc)
     50 		}
     51 		img, err := Decode(bytes.NewReader(data))
     52 		if err != nil {
     53 			st.Fatal(err)
     54 		}
     55 		if !imageCompare(img, want) {
     56 			st.Fatal("the decoded icon differs from the source")
     57 		}
     58 	})
     59 
     60 	t.Run("compressed run", func(st *testing.T) {
     61 		// A flat image compresses to repeats, which exercises the other half
     62 		// of the run-length decoder. icsb is 18 pixels, not 16.
     63 		const icsbSide = 18
     64 		planes := make([]byte, icsbSide*icsbSide*4)
     65 		for i := range planes {
     66 			planes[i] = 0xFF
     67 		}
     68 		data := file(encodeElement("icsb", append([]byte("ARGB"), packRLE(planes)...)))
     69 		img, err := Decode(bytes.NewReader(data))
     70 		if err != nil {
     71 			st.Fatal(err)
     72 		}
     73 		if got := img.Bounds().Dx(); got != 18 {
     74 			st.Fatalf("decoded a %dpx icon, want 18", got)
     75 		}
     76 		if got := color.NRGBAModel.Convert(img.At(9, 9)).(color.NRGBA); got != (color.NRGBA{0xFF, 0xFF, 0xFF, 0xFF}) {
     77 			st.Fatalf("centre = %v, want opaque white", got)
     78 		}
     79 	})
     80 
     81 	t.Run("png in an ARGB capable type", func(st *testing.T) {
     82 		// These types hold either format, so the payload has to decide.
     83 		data := file(encodeElement("ic05", pngBytes(t, 32)))
     84 		desc, err := Probe(bytes.NewReader(data))
     85 		if err != nil {
     86 			st.Fatal(err)
     87 		}
     88 		if desc[0].ImageFormat != ImageFormatPNG {
     89 			st.Fatalf("format = %s, want PNG", desc[0].ImageFormat)
     90 		}
     91 		if _, err := Decode(bytes.NewReader(data)); err != nil {
     92 			st.Fatal(err)
     93 		}
     94 	})
     95 
     96 	t.Run("truncated planes", func(st *testing.T) {
     97 		data := file(encodeElement("ic04", []byte("ARGB\x01\x02\x03")))
     98 		if _, err := Decode(bytes.NewReader(data)); !errors.Is(err, ErrMalformed) {
     99 			st.Fatalf("error = %v, want ErrMalformed", err)
    100 		}
    101 	})
    102 }