...

Source file src/golang.org/x/image/ccitt/reader_test.go

Documentation: golang.org/x/image/ccitt

     1  // Copyright 2019 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  
     5  package ccitt
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"image"
    11  	"image/png"
    12  	"io"
    13  	"io/ioutil"
    14  	"math/rand"
    15  	"os"
    16  	"path/filepath"
    17  	"reflect"
    18  	"strings"
    19  	"testing"
    20  	"unsafe"
    21  )
    22  
    23  func compareImages(t *testing.T, img0 image.Image, img1 image.Image) {
    24  	t.Helper()
    25  
    26  	b0 := img0.Bounds()
    27  	b1 := img1.Bounds()
    28  	if b0 != b1 {
    29  		t.Fatalf("bounds differ: %v vs %v", b0, b1)
    30  	}
    31  
    32  	for y := b0.Min.Y; y < b0.Max.Y; y++ {
    33  		for x := b0.Min.X; x < b0.Max.X; x++ {
    34  			c0 := img0.At(x, y)
    35  			c1 := img1.At(x, y)
    36  			if c0 != c1 {
    37  				t.Fatalf("pixel at (%d, %d) differs: %v vs %v", x, y, c0, c1)
    38  			}
    39  		}
    40  	}
    41  }
    42  
    43  func decodePNG(fileName string) (image.Image, error) {
    44  	f, err := os.Open(fileName)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	defer f.Close()
    49  	return png.Decode(f)
    50  }
    51  
    52  // simpleHB is a simple implementation of highBits.
    53  func simpleHB(dst []byte, src []byte, invert bool) (d int, s int) {
    54  	for d < len(dst) {
    55  		numToPack := len(src) - s
    56  		if numToPack <= 0 {
    57  			break
    58  		} else if numToPack > 8 {
    59  			numToPack = 8
    60  		}
    61  
    62  		byteValue := byte(0)
    63  		if invert {
    64  			byteValue = 0xFF >> uint(numToPack)
    65  		}
    66  		for n := 0; n < numToPack; n++ {
    67  			byteValue |= (src[s] & 0x80) >> uint(n)
    68  			s++
    69  		}
    70  		dst[d] = byteValue
    71  		d++
    72  	}
    73  	return d, s
    74  }
    75  
    76  func TestHighBits(t *testing.T) {
    77  	rng := rand.New(rand.NewSource(1))
    78  	dst0 := make([]byte, 3)
    79  	dst1 := make([]byte, 3)
    80  	src := make([]byte, 20)
    81  
    82  	for r := 0; r < 1000; r++ {
    83  		numDst := rng.Intn(len(dst0) + 1)
    84  		randomByte := byte(rng.Intn(256))
    85  		for i := 0; i < numDst; i++ {
    86  			dst0[i] = randomByte
    87  			dst1[i] = randomByte
    88  		}
    89  
    90  		numSrc := rng.Intn(len(src) + 1)
    91  		for i := 0; i < numSrc; i++ {
    92  			src[i] = byte(rng.Intn(256))
    93  		}
    94  
    95  		invert := rng.Intn(2) == 0
    96  
    97  		d0, s0 := highBits(dst0[:numDst], src[:numSrc], invert)
    98  		d1, s1 := simpleHB(dst1[:numDst], src[:numSrc], invert)
    99  
   100  		if (d0 != d1) || (s0 != s1) || !bytes.Equal(dst0[:numDst], dst1[:numDst]) {
   101  			srcHighBits := make([]byte, numSrc)
   102  			for i := range srcHighBits {
   103  				srcHighBits[i] = src[i] >> 7
   104  			}
   105  
   106  			t.Fatalf("r=%d, numDst=%d, numSrc=%d, invert=%t:\nsrcHighBits=%d\n"+
   107  				"got  d=%d, s=%d, bytes=[% 02X]\n"+
   108  				"want d=%d, s=%d, bytes=[% 02X]",
   109  				r, numDst, numSrc, invert, srcHighBits,
   110  				d0, s0, dst0[:numDst],
   111  				d1, s1, dst1[:numDst],
   112  			)
   113  		}
   114  	}
   115  }
   116  
   117  func BenchmarkHighBits(b *testing.B) {
   118  	rng := rand.New(rand.NewSource(1))
   119  	dst := make([]byte, 1024)
   120  	src := make([]byte, 7777)
   121  	for i := range src {
   122  		src[i] = uint8(rng.Intn(256))
   123  	}
   124  
   125  	b.ResetTimer()
   126  	for n := 0; n < b.N; n++ {
   127  		highBits(dst, src, false)
   128  		highBits(dst, src, true)
   129  	}
   130  }
   131  
   132  func TestMaxCodeLength(t *testing.T) {
   133  	br := bitReader{}
   134  	size := unsafe.Sizeof(br.bits)
   135  	size *= 8 // Convert from bytes to bits.
   136  
   137  	// Check that the size of the bitReader.bits field is large enough to hold
   138  	// nextBitMaxNBits bits.
   139  	if size < nextBitMaxNBits {
   140  		t.Fatalf("size: got %d, want >= %d", size, nextBitMaxNBits)
   141  	}
   142  
   143  	// Check that bitReader.nextBit will always leave enough spare bits in the
   144  	// bitReader.bits field such that the decode function can unread up to
   145  	// maxCodeLength bits.
   146  	if want := size - nextBitMaxNBits; maxCodeLength > want {
   147  		t.Fatalf("maxCodeLength: got %d, want <= %d", maxCodeLength, want)
   148  	}
   149  
   150  	// The decode function also assumes that, when saving bits to possibly
   151  	// unread later, those bits fit inside a uint32.
   152  	if maxCodeLength > 32 {
   153  		t.Fatalf("maxCodeLength: got %d, want <= %d", maxCodeLength, 32)
   154  	}
   155  }
   156  
   157  func testDecodeTable(t *testing.T, decodeTable [][2]int16, codes []code, values []uint32) {
   158  	// Build a map from values to codes.
   159  	m := map[uint32]string{}
   160  	for _, code := range codes {
   161  		m[code.val] = code.str
   162  	}
   163  
   164  	// Build the encoded form of those values in MSB order.
   165  	enc := []byte(nil)
   166  	bits := uint8(0)
   167  	nBits := uint32(0)
   168  	for _, v := range values {
   169  		code := m[v]
   170  		if code == "" {
   171  			panic("unmapped code")
   172  		}
   173  		for _, c := range code {
   174  			bits |= uint8(c&1) << (7 - nBits)
   175  			nBits++
   176  			if nBits == 8 {
   177  				enc = append(enc, bits)
   178  				bits = 0
   179  				nBits = 0
   180  				continue
   181  			}
   182  		}
   183  	}
   184  	if nBits > 0 {
   185  		enc = append(enc, bits)
   186  	}
   187  
   188  	// Decode that encoded form.
   189  	got := []uint32(nil)
   190  	r := &bitReader{
   191  		r:     bytes.NewReader(enc),
   192  		order: MSB,
   193  	}
   194  	finalValue := values[len(values)-1]
   195  	for {
   196  		v, err := decode(r, decodeTable)
   197  		if err != nil {
   198  			t.Fatalf("after got=%d: %v", got, err)
   199  		}
   200  		got = append(got, v)
   201  		if v == finalValue {
   202  			break
   203  		}
   204  	}
   205  
   206  	// Check that the round-tripped values were unchanged.
   207  	if !reflect.DeepEqual(got, values) {
   208  		t.Fatalf("\ngot:  %v\nwant: %v", got, values)
   209  	}
   210  }
   211  
   212  func TestModeDecodeTable(t *testing.T) {
   213  	testDecodeTable(t, modeDecodeTable[:], modeCodes, []uint32{
   214  		modePass,
   215  		modeV0,
   216  		modeV0,
   217  		modeVL1,
   218  		modeVR3,
   219  		modeVL2,
   220  		modeExt,
   221  		modeVL1,
   222  		modeH,
   223  		modeVL1,
   224  		modeVL1,
   225  		// The exact value of this final slice element doesn't really matter,
   226  		// except that testDecodeTable assumes that it (the finalValue) is
   227  		// different from every previous element.
   228  		modeVL3,
   229  	})
   230  }
   231  
   232  func TestWhiteDecodeTable(t *testing.T) {
   233  	testDecodeTable(t, whiteDecodeTable[:], whiteCodes, []uint32{
   234  		0, 1, 256, 7, 128, 3, 2560,
   235  	})
   236  }
   237  
   238  func TestBlackDecodeTable(t *testing.T) {
   239  	testDecodeTable(t, blackDecodeTable[:], blackCodes, []uint32{
   240  		63, 64, 63, 64, 64, 63, 22, 1088, 2048, 7, 6, 5, 4, 3, 2, 1, 0,
   241  	})
   242  }
   243  
   244  func TestDecodeInvalidCode(t *testing.T) {
   245  	// The bit stream is:
   246  	// 1 010 000000011011
   247  	// Packing that LSB-first gives:
   248  	// 0b_1101_1000_0000_0101
   249  	src := []byte{0x05, 0xD8}
   250  
   251  	decodeTable := modeDecodeTable[:]
   252  	r := &bitReader{
   253  		r: bytes.NewReader(src),
   254  	}
   255  
   256  	// "1" decodes to the value 2.
   257  	if v, err := decode(r, decodeTable); v != 2 || err != nil {
   258  		t.Fatalf("decode #0: got (%v, %v), want (2, nil)", v, err)
   259  	}
   260  
   261  	// "010" decodes to the value 6.
   262  	if v, err := decode(r, decodeTable); v != 6 || err != nil {
   263  		t.Fatalf("decode #0: got (%v, %v), want (6, nil)", v, err)
   264  	}
   265  
   266  	// "00000001" is an invalid code.
   267  	if v, err := decode(r, decodeTable); v != 0 || err != errInvalidCode {
   268  		t.Fatalf("decode #0: got (%v, %v), want (0, %v)", v, err, errInvalidCode)
   269  	}
   270  
   271  	// The bitReader should not have advanced after encountering an invalid
   272  	// code. The remaining bits should be "000000011011".
   273  	remaining := []byte(nil)
   274  	for {
   275  		bit, err := r.nextBit()
   276  		if err == io.EOF {
   277  			break
   278  		} else if err != nil {
   279  			t.Fatalf("nextBit: %v", err)
   280  		}
   281  		remaining = append(remaining, uint8('0'+bit))
   282  	}
   283  	if got, want := string(remaining), "000000011011"; got != want {
   284  		t.Fatalf("remaining bits: got %q, want %q", got, want)
   285  	}
   286  }
   287  
   288  func testRead(t *testing.T, fileName string, sf SubFormat, align, invert, truncated bool) {
   289  	t.Helper()
   290  
   291  	const width, height = 153, 55
   292  	opts := &Options{
   293  		Align:  align,
   294  		Invert: invert,
   295  	}
   296  
   297  	got := ""
   298  	{
   299  		f, err := os.Open(fileName)
   300  		if err != nil {
   301  			t.Fatalf("Open: %v", err)
   302  		}
   303  		defer f.Close()
   304  		gotBytes, err := ioutil.ReadAll(NewReader(f, MSB, sf, width, height, opts))
   305  		if err != nil {
   306  			t.Fatalf("ReadAll: %v", err)
   307  		}
   308  		got = string(gotBytes)
   309  	}
   310  
   311  	want := ""
   312  	{
   313  		img, err := decodePNG("testdata/bw-gopher.png")
   314  		if err != nil {
   315  			t.Fatalf("decodePNG: %v", err)
   316  		}
   317  		gray, ok := img.(*image.Gray)
   318  		if !ok {
   319  			t.Fatalf("decodePNG: got %T, want *image.Gray", img)
   320  		}
   321  		bounds := gray.Bounds()
   322  		if w := bounds.Dx(); w != width {
   323  			t.Fatalf("width: got %d, want %d", w, width)
   324  		}
   325  		if h := bounds.Dy(); h != height {
   326  			t.Fatalf("height: got %d, want %d", h, height)
   327  		}
   328  
   329  		// Prepare to extend each row's width to a multiple of 8, to simplify
   330  		// packing from 1 byte per pixel to 1 bit per pixel.
   331  		extended := make([]byte, (width+7)&^7)
   332  
   333  		wantBytes := []byte(nil)
   334  		for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
   335  			rowPix := gray.Pix[(y-bounds.Min.Y)*gray.Stride:]
   336  			rowPix = rowPix[:width]
   337  			copy(extended, rowPix)
   338  
   339  			// Pack from 1 byte per pixel to 1 bit per pixel, MSB first.
   340  			byteValue := uint8(0)
   341  			for x, pixel := range extended {
   342  				byteValue |= (pixel & 0x80) >> uint(x&7)
   343  				if (x & 7) == 7 {
   344  					wantBytes = append(wantBytes, byteValue)
   345  					byteValue = 0
   346  				}
   347  			}
   348  		}
   349  		want = string(wantBytes)
   350  	}
   351  
   352  	// We expect a width of 153 pixels, which is 20 bytes per row (at 1 bit per
   353  	// pixel, plus 7 final bits of padding). Check that want is 20 * height
   354  	// bytes long, and if got != want, format them to split at every 20 bytes.
   355  
   356  	if n := len(want); n != 20*height {
   357  		t.Fatalf("len(want): got %d, want %d", n, 20*height)
   358  	}
   359  
   360  	format := func(s string) string {
   361  		b := []byte(nil)
   362  		for row := 0; len(s) >= 20; row++ {
   363  			b = append(b, fmt.Sprintf("row%02d: %02X\n", row, s[:20])...)
   364  			s = s[20:]
   365  		}
   366  		if len(s) > 0 {
   367  			b = append(b, fmt.Sprintf("%02X\n", s)...)
   368  		}
   369  		return string(b)
   370  	}
   371  
   372  	if got != want {
   373  		t.Fatalf("got:\n%s\nwant:\n%s", format(got), format(want))
   374  	}
   375  
   376  	// Passing AutoDetectHeight should produce the same output, provided that
   377  	// the input hasn't truncated the trailing sequence of consecutive EOL's
   378  	// that marks the end of the image.
   379  	if !truncated {
   380  		f, err := os.Open(fileName)
   381  		if err != nil {
   382  			t.Fatalf("Open: %v", err)
   383  		}
   384  		defer f.Close()
   385  		adhBytes, err := ioutil.ReadAll(NewReader(f, MSB, sf, width, AutoDetectHeight, opts))
   386  		if err != nil {
   387  			t.Fatalf("ReadAll: %v", err)
   388  		}
   389  		if s := string(adhBytes); s != want {
   390  			t.Fatalf("AutoDetectHeight produced different output.\n"+
   391  				"height=%d:\n%s\nheight=%d:\n%s",
   392  				AutoDetectHeight, format(s), height, format(want))
   393  		}
   394  	}
   395  }
   396  
   397  func TestRead(t *testing.T) {
   398  	for _, fileName := range []string{
   399  		"testdata/bw-gopher.ccitt_group3",
   400  		"testdata/bw-gopher-aligned.ccitt_group3",
   401  		"testdata/bw-gopher-inverted.ccitt_group3",
   402  		"testdata/bw-gopher-inverted-aligned.ccitt_group3",
   403  		"testdata/bw-gopher.ccitt_group4",
   404  		"testdata/bw-gopher-aligned.ccitt_group4",
   405  		"testdata/bw-gopher-inverted.ccitt_group4",
   406  		"testdata/bw-gopher-inverted-aligned.ccitt_group4",
   407  		"testdata/bw-gopher-truncated0.ccitt_group3",
   408  		"testdata/bw-gopher-truncated0.ccitt_group4",
   409  		"testdata/bw-gopher-truncated1.ccitt_group3",
   410  		"testdata/bw-gopher-truncated1.ccitt_group4",
   411  	} {
   412  		subFormat := Group3
   413  		if strings.HasSuffix(fileName, "group4") {
   414  			subFormat = Group4
   415  		}
   416  		align := strings.Contains(fileName, "aligned")
   417  		invert := strings.Contains(fileName, "inverted")
   418  		truncated := strings.Contains(fileName, "truncated")
   419  		testRead(t, fileName, subFormat, align, invert, truncated)
   420  	}
   421  }
   422  
   423  func TestDecodeIntoGray(t *testing.T) {
   424  	for _, tt := range []struct {
   425  		fileName string
   426  		sf       SubFormat
   427  		w, h     int
   428  	}{
   429  		{"testdata/bw-gopher.ccitt_group3", Group3, 153, 55},
   430  		{"testdata/bw-gopher.ccitt_group4", Group4, 153, 55},
   431  		{"testdata/bw-gopher-truncated0.ccitt_group3", Group3, 153, 55},
   432  		{"testdata/bw-gopher-truncated0.ccitt_group4", Group4, 153, 55},
   433  		{"testdata/bw-gopher-truncated1.ccitt_group3", Group3, 153, 55},
   434  		{"testdata/bw-gopher-truncated1.ccitt_group4", Group4, 153, 55},
   435  	} {
   436  		t.Run(tt.fileName, func(t *testing.T) {
   437  			testDecodeIntoGray(t, tt.fileName, MSB, tt.sf, tt.w, tt.h, nil)
   438  		})
   439  	}
   440  }
   441  
   442  func testDecodeIntoGray(t *testing.T, fileName string, order Order, sf SubFormat, width int, height int, opts *Options) {
   443  	t.Helper()
   444  
   445  	f, err := os.Open(filepath.FromSlash(fileName))
   446  	if err != nil {
   447  		t.Fatalf("Open: %v", err)
   448  	}
   449  	defer f.Close()
   450  
   451  	got := image.NewGray(image.Rect(0, 0, width, height))
   452  	if err := DecodeIntoGray(got, f, order, sf, opts); err != nil {
   453  		t.Fatalf("DecodeIntoGray: %v", err)
   454  	}
   455  
   456  	want, err := decodePNG("testdata/bw-gopher.png")
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  
   461  	compareImages(t, got, want)
   462  }
   463  

View as plain text