git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

ccitt/reader_test.go (11.3K)

  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 }