Skip to content

Commit

Permalink
reuse more of the image/jpeg exports
Browse files Browse the repository at this point in the history
  • Loading branch information
lukechampine committed Sep 19, 2017
1 parent 248b229 commit c6373ec
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 70 deletions.
19 changes: 10 additions & 9 deletions huffman.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package jsteg

import (
"image/jpeg"
"io"
)

Expand Down Expand Up @@ -40,7 +41,7 @@ type huffman struct {

// errShortHuffmanData means that an unexpected EOF occurred while decoding
// Huffman data.
var errShortHuffmanData = FormatError("short Huffman data")
var errShortHuffmanData = jpeg.FormatError("short Huffman data")

// ensureNBits reads bytes from the byte buffer to ensure that d.bits.n is at
// least n. For best performance (avoiding function calls inside hot loops),
Expand Down Expand Up @@ -91,19 +92,19 @@ func (d *decoder) receiveExtend(t uint8) (int32, error) {
func (d *decoder) processDHT(n int) error {
for n > 0 {
if n < 17 {
return FormatError("DHT has wrong length")
return jpeg.FormatError("DHT has wrong length")
}
if err := d.readFull(d.tmp[:17]); err != nil {
return err
}
tc := d.tmp[0] >> 4
if tc > maxTc {
return FormatError("bad Tc value")
return jpeg.FormatError("bad Tc value")
}
th := d.tmp[0] & 0x0f
// The baseline th <= 1 restriction is specified in table B.5.
if th > maxTh || (d.baseline && th > 1) {
return FormatError("bad Th value")
return jpeg.FormatError("bad Th value")
}
h := &d.huff[tc][th]

Expand All @@ -117,14 +118,14 @@ func (d *decoder) processDHT(n int) error {
h.nCodes += nCodes[i]
}
if h.nCodes == 0 {
return FormatError("Huffman table has zero length")
return jpeg.FormatError("Huffman table has zero length")
}
if h.nCodes > maxNCodes {
return FormatError("Huffman table has excessive length")
return jpeg.FormatError("Huffman table has excessive length")
}
n -= int(h.nCodes) + 17
if n < 0 {
return FormatError("DHT has wrong length")
return jpeg.FormatError("DHT has wrong length")
}
if err := d.readFull(h.vals[:h.nCodes]); err != nil {
return err
Expand Down Expand Up @@ -177,7 +178,7 @@ func (d *decoder) processDHT(n int) error {
// decoded according to h.
func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
if h.nCodes == 0 {
return 0, FormatError("uninitialized Huffman table")
return 0, jpeg.FormatError("uninitialized Huffman table")
}

if d.bits.n < 8 {
Expand Down Expand Up @@ -218,5 +219,5 @@ slowPath:
}
code <<= 1
}
return 0, FormatError("bad Huffman code")
return 0, jpeg.FormatError("bad Huffman code")
}
2 changes: 1 addition & 1 deletion jsteg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestRevealProgressive(t *testing.T) {
defer f.Close()

_, err = Reveal(f)
if _, ok := err.(UnsupportedError); !ok {
if _, ok := err.(jpeg.UnsupportedError); !ok {
t.Fatal("expected UnsupportedError, got", err)
}
}
Expand Down
60 changes: 21 additions & 39 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,11 @@ package jsteg

import (
"image"
"image/jpeg"
"io"
)

// TODO(nigeltao): fix up the doc comment style so that sentences start with
// the name of the type or function that they annotate.

// A FormatError reports that the input is not a valid JPEG.
type FormatError string

func (e FormatError) Error() string { return "invalid JPEG format: " + string(e) }

// An UnsupportedError reports that the input uses a valid but unimplemented JPEG feature.
type UnsupportedError string

func (e UnsupportedError) Error() string { return "unsupported JPEG feature: " + string(e) }

var errUnsupportedSubsamplingRatio = UnsupportedError("luma/chroma subsampling ratio")
var errUnsupportedSubsamplingRatio = jpeg.UnsupportedError("luma/chroma subsampling ratio")

// Component specification, specified in section B.2.2.
type component struct {
Expand Down Expand Up @@ -78,12 +66,6 @@ var unzig = [blockSize]int{
53, 60, 61, 54, 47, 55, 62, 63,
}

// Deprecated: Reader is deprecated.
type Reader interface {
io.ByteReader
io.Reader
}

// bits holds the unprocessed bits that have been taken from the byte-stream.
// The n least significant bits of a form the unread bits, to be read in MSB to
// LSB order.
Expand Down Expand Up @@ -135,8 +117,8 @@ type decoder struct {
tmp [2 * blockSize]byte

// steganography
data []byte
databit uint
data []byte
databit uint
}

// fill fills up the d.bytes.buf buffer from the underlying io.Reader. It
Expand Down Expand Up @@ -192,7 +174,7 @@ func (d *decoder) readByte() (x byte, err error) {

// errMissingFF00 means that readByteStuffedByte encountered an 0xff byte (a
// marker byte) that wasn't the expected byte-stuffed sequence 0xff, 0x00.
var errMissingFF00 = FormatError("missing 0xff00 sequence")
var errMissingFF00 = jpeg.FormatError("missing 0xff00 sequence")

// readByteStuffedByte is like readByte but is for byte-stuffed Huffman data.
func (d *decoder) readByteStuffedByte() (x byte, err error) {
Expand Down Expand Up @@ -295,7 +277,7 @@ func (d *decoder) ignore(n int) error {
// Specified in section B.2.2.
func (d *decoder) processSOF(n int) error {
if d.nComp != 0 {
return FormatError("multiple SOF markers")
return jpeg.FormatError("multiple SOF markers")
}
switch n {
case 6 + 3*1: // Grayscale image.
Expand All @@ -305,19 +287,19 @@ func (d *decoder) processSOF(n int) error {
case 6 + 3*4: // YCbCrK or CMYK image.
d.nComp = 4
default:
return UnsupportedError("number of components")
return jpeg.UnsupportedError("number of components")
}
if err := d.readFull(d.tmp[:n]); err != nil {
return err
}
// We only support 8-bit precision.
if d.tmp[0] != 8 {
return UnsupportedError("precision")
return jpeg.UnsupportedError("precision")
}
d.height = int(d.tmp[1])<<8 + int(d.tmp[2])
d.width = int(d.tmp[3])<<8 + int(d.tmp[4])
if int(d.tmp[5]) != d.nComp {
return FormatError("SOF has wrong length")
return jpeg.FormatError("SOF has wrong length")
}

for i := 0; i < d.nComp; i++ {
Expand All @@ -326,19 +308,19 @@ func (d *decoder) processSOF(n int) error {
// the values of C_1 through C_(i-1)".
for j := 0; j < i; j++ {
if d.comp[i].c == d.comp[j].c {
return FormatError("repeated component identifier")
return jpeg.FormatError("repeated component identifier")
}
}

d.comp[i].tq = d.tmp[8+3*i]
if d.comp[i].tq > maxTq {
return FormatError("bad Tq value")
return jpeg.FormatError("bad Tq value")
}

hv := d.tmp[7+3*i]
h, v := int(hv>>4), int(hv&0x0f)
if h < 1 || 4 < h || v < 1 || 4 < v {
return FormatError("luma/chroma subsampling ratio")
return jpeg.FormatError("luma/chroma subsampling ratio")
}
if h == 3 || v == 3 {
return errUnsupportedSubsamplingRatio
Expand Down Expand Up @@ -427,11 +409,11 @@ loop:
}
tq := x & 0x0f
if tq > maxTq {
return FormatError("bad Tq value")
return jpeg.FormatError("bad Tq value")
}
switch x >> 4 {
default:
return FormatError("bad Pq value")
return jpeg.FormatError("bad Pq value")
case 0:
if n < blockSize {
break loop
Expand All @@ -457,15 +439,15 @@ loop:
}
}
if n != 0 {
return FormatError("DQT has wrong length")
return jpeg.FormatError("DQT has wrong length")
}
return nil
}

// Specified in section B.2.4.4.
func (d *decoder) processDRI(n int) error {
if n != 2 {
return FormatError("DRI has wrong length")
return jpeg.FormatError("DRI has wrong length")
}
if err := d.readFull(d.tmp[:2]); err != nil {
return err
Expand Down Expand Up @@ -521,7 +503,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) ([]byte, error) {
return nil, err
}
if d.tmp[0] != 0xff || d.tmp[1] != soiMarker {
return nil, FormatError("missing SOI marker")
return nil, jpeg.FormatError("missing SOI marker")
}

// Process the remaining segments until the End Of Image marker.
Expand Down Expand Up @@ -590,13 +572,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) ([]byte, error) {
}
n := int(d.tmp[0])<<8 + int(d.tmp[1]) - 2
if n < 0 {
return nil, FormatError("short segment length")
return nil, jpeg.FormatError("short segment length")
}

switch marker {
case sof0Marker, sof1Marker, sof2Marker:
if marker == sof2Marker {
return nil, UnsupportedError("progressive decoding")
return nil, jpeg.UnsupportedError("progressive decoding")
}
d.baseline = marker == sof0Marker
err = d.processSOF(n)
Expand Down Expand Up @@ -634,9 +616,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) ([]byte, error) {
if app0Marker <= marker && marker <= app15Marker || marker == comMarker {
err = d.ignore(n)
} else if marker < 0xc0 { // See Table B.1 "Marker code assignments".
err = FormatError("unknown marker")
err = jpeg.FormatError("unknown marker")
} else {
err = UnsupportedError("unknown marker")
err = jpeg.UnsupportedError("unknown marker")
}
}
if err != nil {
Expand Down
22 changes: 12 additions & 10 deletions scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@

package jsteg

import "image/jpeg"

const blockSize = 64 // A DCT block is 8x8.

type block [blockSize]int32

// Specified in section B.2.3.
func (d *decoder) processSOS(n int) error {
if d.nComp == 0 {
return FormatError("missing SOF marker")
return jpeg.FormatError("missing SOF marker")
}
if n < 6 || 4+2*d.nComp < n || n%2 != 0 {
return FormatError("SOS has wrong length")
return jpeg.FormatError("SOS has wrong length")
}
if err := d.readFull(d.tmp[:n]); err != nil {
return err
}
nComp := int(d.tmp[0])
if n != 4+2*nComp {
return FormatError("SOS length inconsistent with number of components")
return jpeg.FormatError("SOS length inconsistent with number of components")
}
var scan [maxComponents]struct {
compIndex uint8
Expand All @@ -38,7 +40,7 @@ func (d *decoder) processSOS(n int) error {
}
}
if compIndex < 0 {
return FormatError("unknown component selector")
return jpeg.FormatError("unknown component selector")
}
scan[i].compIndex = uint8(compIndex)
// Section B.2.3 states that "the value of Cs_j shall be different from
Expand All @@ -48,25 +50,25 @@ func (d *decoder) processSOS(n int) error {
// into d.comp are unique.
for j := 0; j < i; j++ {
if scan[i].compIndex == scan[j].compIndex {
return FormatError("repeated component selector")
return jpeg.FormatError("repeated component selector")
}
}
totalHV += d.comp[compIndex].h * d.comp[compIndex].v

// The baseline t <= 1 restriction is specified in table B.3.
scan[i].td = d.tmp[2+2*i] >> 4
if t := scan[i].td; t > maxTh || (d.baseline && t > 1) {
return FormatError("bad Td value")
return jpeg.FormatError("bad Td value")
}
scan[i].ta = d.tmp[2+2*i] & 0x0f
if t := scan[i].ta; t > maxTh || (d.baseline && t > 1) {
return FormatError("bad Ta value")
return jpeg.FormatError("bad Ta value")
}
}
// Section B.2.3 states that if there is more than one component then the
// total H*V values in a scan must be <= 10.
if d.nComp > 1 && totalHV > 10 {
return FormatError("total sampling factors too large")
return jpeg.FormatError("total sampling factors too large")
}

// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
Expand All @@ -89,7 +91,7 @@ func (d *decoder) processSOS(n int) error {
return err
}
if value > 16 {
return UnsupportedError("excessive DC component")
return jpeg.UnsupportedError("excessive DC component")
}
if _, err = d.receiveExtend(value); err != nil {
return err
Expand Down Expand Up @@ -140,7 +142,7 @@ func (d *decoder) processSOS(n int) error {
return err
}
if d.tmp[0] != 0xff || d.tmp[1] != expectedRST {
return FormatError("bad RST marker")
return jpeg.FormatError("bad RST marker")
}
expectedRST++
if expectedRST == rst7Marker+1 {
Expand Down
14 changes: 3 additions & 11 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"image"
"image/color"
"image/jpeg"
"io"
)

Expand Down Expand Up @@ -582,23 +583,14 @@ func (e *encoder) writeSOS(m image.Image) {
e.emit(0x7f, 7)
}

// DefaultQuality is the default quality encoding parameter.
const DefaultQuality = 75

// Options are the encoding parameters.
// Quality ranges from 1 to 100 inclusive, higher is better.
type Options struct {
Quality int
}

// ErrTooSmall is returned if the image is too small to hold the requested
// payload.
var ErrTooSmall = errors.New("image is too small to hold the requested payload")

// Hide writes the Image m to w in JPEG 4:2:0 baseline format with the given
// options, hiding the bits of data in the LSB of each block. Default
// parameters are used if a nil *Options is passed.
func Hide(w io.Writer, m image.Image, data []byte, o *Options) error {
func Hide(w io.Writer, m image.Image, data []byte, o *jpeg.Options) error {
b := m.Bounds()
if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
return errors.New("jpeg: image is too large to encode")
Expand All @@ -611,7 +603,7 @@ func Hide(w io.Writer, m image.Image, data []byte, o *Options) error {
e.w = bufio.NewWriter(w)
}
// Clip quality to [1, 100].
quality := DefaultQuality
quality := jpeg.DefaultQuality
if o != nil {
quality = o.Quality
if quality < 1 {
Expand Down

0 comments on commit c6373ec

Please sign in to comment.