diff options
Diffstat (limited to 'vendor/golang.org/x/image/ccitt/reader.go')
-rw-r--r-- | vendor/golang.org/x/image/ccitt/reader.go | 156 |
1 files changed, 127 insertions, 29 deletions
diff --git a/vendor/golang.org/x/image/ccitt/reader.go b/vendor/golang.org/x/image/ccitt/reader.go index 16bd495d5..340de0536 100644 --- a/vendor/golang.org/x/image/ccitt/reader.go +++ b/vendor/golang.org/x/image/ccitt/reader.go @@ -16,6 +16,7 @@ import ( ) var ( + errIncompleteCode = errors.New("ccitt: incomplete code") errInvalidBounds = errors.New("ccitt: invalid bounds") errInvalidCode = errors.New("ccitt: invalid code") errInvalidMode = errors.New("ccitt: invalid mode") @@ -48,6 +49,10 @@ const ( Group4 ) +// AutoDetectHeight is passed as the height argument to NewReader to indicate +// that the image height (the number of rows) is not known in advance. +const AutoDetectHeight = -1 + // Options are optional parameters. type Options struct { // Align means that some variable-bit-width codes are byte-aligned. @@ -205,10 +210,14 @@ func decode(b *bitReader, decodeTable [][2]int16) (uint32, error) { for { bit, err := b.nextBit() if err != nil { + if err == io.EOF { + err = errIncompleteCode + } return 0, err } bitsRead |= bit << (63 - nBitsRead) nBitsRead++ + // The "&1" is redundant, but can eliminate a bounds check. state = int32(decodeTable[state][bit&1]) if state < 0 { @@ -222,6 +231,35 @@ func decode(b *bitReader, decodeTable [][2]int16) (uint32, error) { } } +// decodeEOL decodes the 12-bit EOL code 0000_0000_0001. +func decodeEOL(b *bitReader) error { + nBitsRead, bitsRead := uint32(0), uint64(0) + for { + bit, err := b.nextBit() + if err != nil { + if err == io.EOF { + err = errMissingEOL + } + return err + } + bitsRead |= bit << (63 - nBitsRead) + nBitsRead++ + + if nBitsRead < 12 { + if bit&1 == 0 { + continue + } + } else if bit&1 != 0 { + return nil + } + + // Unread the bits we've read, then return errMissingEOL. + b.bits = (b.bits >> nBitsRead) | bitsRead + b.nBits += nBitsRead + return errMissingEOL + } +} + type reader struct { br bitReader subFormat SubFormat @@ -231,7 +269,10 @@ type reader struct { // rowsRemaining starts at the image height in pixels, when the reader is // driven through the io.Reader interface, and decrements to zero as rows - // are decoded. When driven through DecodeIntoGray, this field is unused. + // are decoded. Alternatively, it may be negative if the image height is + // not known in advance at the time of the NewReader call. + // + // When driven through DecodeIntoGray, this field is unused. rowsRemaining int // curr and prev hold the current and previous rows. Each element is either @@ -269,6 +310,19 @@ type reader struct { // seenStartOfImage is whether we've called the startDecode method. seenStartOfImage bool + // truncated is whether the input is missing the final 6 consecutive EOL's + // (for Group3) or 2 consecutive EOL's (for Group4). Omitting that trailer + // (but otherwise padding to a byte boundary, with either all 0 bits or all + // 1 bits) is invalid according to the spec, but happens in practice when + // exporting from Adobe Acrobat to TIFF + CCITT. This package silently + // ignores the format error for CCITT input that has been truncated in that + // fashion, returning the full decoded image. + // + // Detecting trailer truncation (just after the final row of pixels) + // requires knowing which row is the final row, and therefore does not + // trigger if the image height is not known in advance. + truncated bool + // readErr is a sticky error for the Read method. readErr error } @@ -294,17 +348,50 @@ func (z *reader) Read(p []byte) (int, error) { // Decode the next row, if necessary. if z.atStartOfRow { - if z.rowsRemaining <= 0 { - if z.readErr = z.finishDecode(); z.readErr != nil { + if z.rowsRemaining < 0 { + // We do not know the image height in advance. See if the next + // code is an EOL. If it is, it is consumed. If it isn't, the + // bitReader shouldn't advance along the bit stream, and we + // simply decode another row of pixel data. + // + // For the Group4 subFormat, we may need to align to a byte + // boundary. For the Group3 subFormat, the previous z.decodeRow + // call (or z.startDecode call) has already consumed one of the + // 6 consecutive EOL's. The next EOL is actually the second of + // 6, in the middle, and we shouldn't align at that point. + if z.align && (z.subFormat == Group4) { + z.br.alignToByteBoundary() + } + + if err := z.decodeEOL(); err == errMissingEOL { + // No-op. It's another row of pixel data. + } else if err != nil { + z.readErr = err + break + } else { + if z.readErr = z.finishDecode(true); z.readErr != nil { + break + } + z.readErr = io.EOF + break + } + + } else if z.rowsRemaining == 0 { + // We do know the image height in advance, and we have already + // decoded exactly that many rows. + if z.readErr = z.finishDecode(false); z.readErr != nil { break } z.readErr = io.EOF break + + } else { + z.rowsRemaining-- } - if z.readErr = z.decodeRow(); z.readErr != nil { + + if z.readErr = z.decodeRow(z.rowsRemaining == 0); z.readErr != nil { break } - z.rowsRemaining-- } // Pack from z.curr (1 byte per pixel) to p (1 bit per pixel). @@ -351,32 +438,44 @@ func (z *reader) startDecode() error { return nil } -func (z *reader) finishDecode() error { +func (z *reader) finishDecode(alreadySeenEOL bool) error { numberOfEOLs := 0 switch z.subFormat { case Group3: + if z.truncated { + return nil + } // The stream ends with a RTC (Return To Control) of 6 consecutive // EOL's, but we should have already just seen an EOL, either in // z.startDecode (for a zero-height image) or in z.decodeRow. numberOfEOLs = 5 case Group4: - // The stream ends with two EOL's, the first of which is possibly - // byte-aligned. - numberOfEOLs = 2 - if err := z.decodeEOL(); err == nil { - numberOfEOLs-- - } else if err == errInvalidCode { - // Try again, this time starting from a byte boundary. + autoDetectHeight := z.rowsRemaining < 0 + if autoDetectHeight { + // Aligning to a byte boundary was already handled by reader.Read. + } else if z.align { z.br.alignToByteBoundary() - } else { + } + // The stream ends with two EOL's. If the first one is missing, and we + // had an explicit image height, we just assume that the trailing two + // EOL's were truncated and return a nil error. + if err := z.decodeEOL(); err != nil { + if (err == errMissingEOL) && !autoDetectHeight { + z.truncated = true + return nil + } return err } + numberOfEOLs = 1 default: return errUnsupportedSubFormat } + if alreadySeenEOL { + numberOfEOLs-- + } for ; numberOfEOLs > 0; numberOfEOLs-- { if err := z.decodeEOL(); err != nil { return err @@ -386,19 +485,10 @@ func (z *reader) finishDecode() error { } func (z *reader) decodeEOL() error { - // TODO: EOL doesn't have to be in the modeDecodeTable. It could be in its - // own table, or we could just hard-code it, especially if we might need to - // cater for optional byte-alignment, or an arbitrary number (potentially - // more than 8) of 0-valued padding bits. - if mode, err := decode(&z.br, modeDecodeTable[:]); err != nil { - return err - } else if mode != modeEOL { - return errMissingEOL - } - return nil + return decodeEOL(&z.br) } -func (z *reader) decodeRow() error { +func (z *reader) decodeRow(finalRow bool) error { z.wi = 0 z.atStartOfRow = true z.penColorIsWhite = true @@ -414,7 +504,12 @@ func (z *reader) decodeRow() error { return err } } - return z.decodeEOL() + err := z.decodeEOL() + if finalRow && (err == errMissingEOL) { + z.truncated = true + return nil + } + return err case Group4: for ; z.wi < len(z.curr); z.atStartOfRow = false { @@ -654,13 +749,13 @@ func DecodeIntoGray(dst *image.Gray, r io.Reader, order Order, sf SubFormat, opt for y := bounds.Min.Y; y < bounds.Max.Y; y++ { p := (y - bounds.Min.Y) * dst.Stride z.curr = dst.Pix[p : p+width] - if err := z.decodeRow(); err != nil { + if err := z.decodeRow(y+1 == bounds.Max.Y); err != nil { return err } z.curr, z.prev = nil, z.curr } - if err := z.finishDecode(); err != nil { + if err := z.finishDecode(false); err != nil { return err } @@ -677,9 +772,12 @@ func DecodeIntoGray(dst *image.Gray, r io.Reader, order Order, sf SubFormat, opt // NewReader returns an io.Reader that decodes the CCITT-formatted data in r. // The resultant byte stream is one bit per pixel (MSB first), with 1 meaning // white and 0 meaning black. Each row in the result is byte-aligned. +// +// A negative height, such as passing AutoDetectHeight, means that the image +// height is not known in advance. A negative width is invalid. func NewReader(r io.Reader, order Order, sf SubFormat, width int, height int, opts *Options) io.Reader { readErr := error(nil) - if (width < 0) || (height < 0) { + if width < 0 { readErr = errInvalidBounds } else if width > maxWidth { readErr = errUnsupportedWidth |