mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-02 22:17:06 +08:00

* Fixed issue #243. Added optimize integration tests. * Minor style change. * XObjImage getParamsDict updates Columns and Rows. * Added doc file for the optimize/tests package. * UpdateParams for CCITTFax Encoder accepts Width and Height also. Removed GetParamsDict Columns and Rows parameters from model.Image and model.XObjImage. * Fix i386 issue for the jbig2 arithmetic encoder. * Added 386 architecture to the .travis/cross_build.sh
636 lines
13 KiB
Go
636 lines
13 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package arithmetic
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/bitmap"
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/errors"
|
|
)
|
|
|
|
// Encoder is the jbig2 arithmetic encoder context.
|
|
type Encoder struct {
|
|
c uint32
|
|
a uint16
|
|
ct, b uint8
|
|
bp int
|
|
ec int
|
|
|
|
// the list of output chunks, not including the current one
|
|
outputChunks [][]byte
|
|
// current output chunk
|
|
outbuf []byte
|
|
// number of bytes used in outbuf
|
|
outbufUsed int
|
|
context *codingContext
|
|
intCtx [13]*codingContext
|
|
iaidCtx *codingContext
|
|
}
|
|
|
|
// New creates new initialized arithmetic encoder.
|
|
func New() *Encoder {
|
|
e := &Encoder{}
|
|
e.Init()
|
|
return e
|
|
}
|
|
|
|
// Init initializes a new context.
|
|
func (e *Encoder) Init() {
|
|
e.context = newContext(maxCtx)
|
|
e.a = 0x8000
|
|
e.c = 0
|
|
e.ct = 12
|
|
e.bp = -1
|
|
e.b = 0
|
|
e.outbufUsed = 0
|
|
e.outbuf = make([]byte, outputBufferSize)
|
|
for i := 0; i < len(e.intCtx); i++ {
|
|
e.intCtx[i] = newContext(512)
|
|
}
|
|
e.iaidCtx = nil
|
|
}
|
|
|
|
// DataSize returns the size of encoded data
|
|
func (e *Encoder) DataSize() int {
|
|
return e.dataSize()
|
|
}
|
|
|
|
const tpgdCTX = 0x9b25
|
|
|
|
// EncodeBitmap encodes packed data used for the 1bpp packed format image.
|
|
func (e *Encoder) EncodeBitmap(bm *bitmap.Bitmap, duplicateLineRemoval bool) error {
|
|
common.Log.Trace("Encode Bitmap [%dx%d], %s", bm.Width, bm.Height, bm)
|
|
var (
|
|
ltp, sLtp uint8
|
|
c1, c2, c3 uint16
|
|
b1, b2, b3 byte
|
|
x, thisLineIndex, lastLineIndex int
|
|
thisLine, lastLine []byte
|
|
)
|
|
|
|
for y := 0; y < bm.Height; y++ {
|
|
// reset the bits context values for each row.
|
|
// the temple used by the coder is fixed as '0' with the floating bits in the default locations.
|
|
// reset the byte values which are the values of the upper rows.
|
|
b1, b2 = 0, 0
|
|
if y >= 2 {
|
|
b1 = bm.Data[(y-2)*bm.RowStride]
|
|
}
|
|
if y >= 1 {
|
|
b2 = bm.Data[(y-1)*bm.RowStride]
|
|
// check if the last row was not the same as this one.
|
|
if duplicateLineRemoval {
|
|
// get this line slice data
|
|
thisLineIndex = y * bm.RowStride
|
|
thisLine = bm.Data[thisLineIndex : thisLineIndex+bm.RowStride]
|
|
// get last line slice data
|
|
lastLineIndex = (y - 1) * bm.RowStride
|
|
lastLine = bm.Data[lastLineIndex : lastLineIndex+bm.RowStride]
|
|
if bytes.Equal(thisLine, lastLine) {
|
|
sLtp = ltp ^ 1
|
|
ltp = 1
|
|
} else {
|
|
sLtp = ltp
|
|
ltp = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
if duplicateLineRemoval {
|
|
if err := e.encodeBit(e.context, tpgdCTX, sLtp); err != nil {
|
|
return err
|
|
}
|
|
if ltp != 0 {
|
|
continue
|
|
}
|
|
}
|
|
b3 = bm.Data[y*bm.RowStride]
|
|
|
|
// top three bits are the start of the context
|
|
c1 = uint16(b1 >> 5)
|
|
c2 = uint16(b2 >> 4)
|
|
|
|
// unused bits needs to be removed.
|
|
b1 <<= 3
|
|
b2 <<= 4
|
|
c3 = 0
|
|
|
|
for x = 0; x < bm.Width; x++ {
|
|
tVal := uint32(c1<<11 | c2<<4 | c3)
|
|
v := (b3 & 0x80) >> 7
|
|
|
|
err := e.encodeBit(e.context, tVal, v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c1 <<= 1
|
|
c2 <<= 1
|
|
c3 <<= 1
|
|
|
|
c1 |= uint16((b1 & 0x80) >> 7)
|
|
c2 |= uint16((b2 & 0x80) >> 7)
|
|
c3 |= uint16(v)
|
|
|
|
m := x % 8
|
|
byteNo := x/8 + 1
|
|
if m == 4 && y >= 2 {
|
|
// roll in another byte from two lines up
|
|
b1 = 0
|
|
if byteNo < bm.RowStride {
|
|
b1 = bm.Data[(y-2)*bm.RowStride+byteNo]
|
|
}
|
|
} else {
|
|
b1 <<= 1
|
|
}
|
|
|
|
if m == 3 && y >= 1 {
|
|
b2 = 0
|
|
if byteNo < bm.RowStride {
|
|
b2 = bm.Data[(y-1)*bm.RowStride+byteNo]
|
|
}
|
|
} else {
|
|
b2 <<= 1
|
|
}
|
|
|
|
if m == 7 {
|
|
b3 = 0
|
|
if byteNo < bm.RowStride {
|
|
b3 = bm.Data[y*bm.RowStride+byteNo]
|
|
}
|
|
} else {
|
|
b3 <<= 1
|
|
}
|
|
|
|
c1 &= 31
|
|
c2 &= 127
|
|
c3 &= 15
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeIAID encodes the integer ID 'value'. The symbol code length is the binary length of the value.
|
|
func (e *Encoder) EncodeIAID(symbolCodeLength, value int) (err error) {
|
|
common.Log.Trace("Encode IAID. SymbolCodeLength: '%d', Value: '%d'", symbolCodeLength, value)
|
|
if err = e.encodeIAID(symbolCodeLength, value); err != nil {
|
|
return errors.Wrap(err, "EncodeIAID", "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeInteger encodes the integer 'value' for the given class 'proc'.
|
|
func (e *Encoder) EncodeInteger(proc Class, value int) (err error) {
|
|
common.Log.Trace("Encode Integer:'%d' with Class: '%s'", value, proc)
|
|
if err = e.encodeInteger(proc, value); err != nil {
|
|
return errors.Wrap(err, "EncodeInteger", "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeOOB encodes out of band value for given class process.
|
|
func (e *Encoder) EncodeOOB(proc Class) (err error) {
|
|
common.Log.Trace("Encode OOB with Class: '%s'", proc)
|
|
if err = e.encodeOOB(proc); err != nil {
|
|
return errors.Wrap(err, "EncodeOOB", "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Final flush any remaining arithmetic encoder context to the output
|
|
func (e *Encoder) Final() {
|
|
e.flush()
|
|
}
|
|
|
|
// Flush all the data stored in a context
|
|
func (e *Encoder) Flush() {
|
|
e.outbufUsed = 0
|
|
e.outputChunks = nil
|
|
e.bp = -1
|
|
}
|
|
|
|
// Refine encodes the refinement of an exemplar to a bitmap.
|
|
// It encodes the differences between the template and the target image.
|
|
// The values 'ox, oy' are limited to [-1, 0, 1].
|
|
func (e *Encoder) Refine(iTemp, iTarget *bitmap.Bitmap, ox, oy int) error {
|
|
for y := 0; y < iTarget.Height; y++ {
|
|
var x int
|
|
templateY := y + oy
|
|
var (
|
|
c1, c2, c3, c4, c5 uint16
|
|
b1, b2, b3, b4, b5 byte
|
|
)
|
|
if templateY >= 1 && (templateY-1) < iTemp.Height {
|
|
b1 = iTemp.Data[(templateY-1)*iTemp.RowStride]
|
|
}
|
|
|
|
if templateY >= 0 && templateY < iTemp.Height {
|
|
b2 = iTemp.Data[templateY*iTemp.RowStride]
|
|
}
|
|
|
|
if templateY >= -1 && templateY+1 < iTemp.Height {
|
|
b3 = iTemp.Data[(templateY+1)*iTemp.RowStride]
|
|
}
|
|
|
|
if y >= 1 {
|
|
b4 = iTarget.Data[(y-1)*iTarget.RowStride]
|
|
}
|
|
b5 = iTarget.Data[y*iTarget.RowStride]
|
|
|
|
shiftOffset := uint(6 + ox)
|
|
c1 = uint16(b1 >> shiftOffset)
|
|
c2 = uint16(b2 >> shiftOffset)
|
|
c3 = uint16(b3 >> shiftOffset)
|
|
|
|
c4 = uint16(b4 >> 6)
|
|
|
|
bitsToTrim := uint(2 - ox)
|
|
b1 <<= bitsToTrim
|
|
b2 <<= bitsToTrim
|
|
b3 <<= bitsToTrim
|
|
|
|
b4 <<= 2
|
|
|
|
for x = 0; x < iTarget.Width; x++ {
|
|
tVal := (c1 << 10) | (c2 << 7) | (c3 << 4) | (c4 << 1) | c5
|
|
v := b5 >> 7
|
|
|
|
err := e.encodeBit(e.context, uint32(tVal), v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c1 <<= 1
|
|
c2 <<= 1
|
|
c3 <<= 1
|
|
c4 <<= 1
|
|
|
|
c1 |= uint16(b1 >> 7)
|
|
c2 |= uint16(b2 >> 7)
|
|
c3 |= uint16(b3 >> 7)
|
|
c4 |= uint16(b4 >> 7)
|
|
c5 = uint16(v)
|
|
|
|
m := x % 8
|
|
wordNo := x/8 + 1
|
|
if m == 5+ox {
|
|
b1, b2, b3 = 0, 0, 0
|
|
if wordNo < iTemp.RowStride && templateY >= 1 && (templateY-1) < iTemp.Height {
|
|
b1 = iTemp.Data[(templateY-1)*iTemp.RowStride+wordNo]
|
|
}
|
|
if wordNo < iTemp.RowStride && templateY >= 0 && templateY < iTemp.Height {
|
|
b2 = iTemp.Data[templateY*iTemp.RowStride+wordNo]
|
|
}
|
|
if wordNo < iTemp.RowStride && templateY >= -1 && (templateY+1) < iTemp.Height {
|
|
b3 = iTemp.Data[(templateY+1)*iTemp.RowStride+wordNo]
|
|
}
|
|
} else {
|
|
b1 <<= 1
|
|
b2 <<= 1
|
|
b3 <<= 1
|
|
}
|
|
|
|
if m == 5 && y >= 1 {
|
|
b4 = 0
|
|
if wordNo < iTarget.RowStride {
|
|
b4 = iTarget.Data[(y-1)*iTarget.RowStride+wordNo]
|
|
}
|
|
} else {
|
|
b4 <<= 1
|
|
}
|
|
|
|
if m == 7 {
|
|
b5 = 0
|
|
if wordNo < iTarget.RowStride {
|
|
b5 = iTarget.Data[y*iTarget.RowStride+wordNo]
|
|
}
|
|
} else {
|
|
b5 <<= 1
|
|
}
|
|
|
|
c1 &= 7
|
|
c2 &= 7
|
|
c3 &= 7
|
|
c4 &= 7
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reset the arithmetic coder back to a init state
|
|
func (e *Encoder) Reset() {
|
|
e.a = 0x8000
|
|
e.c = 0
|
|
e.ct = 12
|
|
e.bp = -1
|
|
e.b = 0
|
|
e.iaidCtx = nil
|
|
e.context = newContext(maxCtx)
|
|
}
|
|
|
|
// compile time check for the io.WriterTo interface.
|
|
var _ io.WriterTo = &Encoder{}
|
|
|
|
// WriteTo implements io.WriterTo interface.
|
|
func (e *Encoder) WriteTo(w io.Writer) (int64, error) {
|
|
const processName = "Encoder.WriteTo"
|
|
var total int64
|
|
for i, chunk := range e.outputChunks {
|
|
n, err := w.Write(chunk)
|
|
if err != nil {
|
|
return 0, errors.Wrapf(err, processName, "failed at i'th: '%d' chunk", i)
|
|
}
|
|
total += int64(n)
|
|
}
|
|
|
|
// remove unused bytes.
|
|
e.outbuf = e.outbuf[:e.outbufUsed]
|
|
n, err := w.Write(e.outbuf)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, processName, "buffered chunks")
|
|
}
|
|
total += int64(n)
|
|
return total, nil
|
|
}
|
|
|
|
func (e *Encoder) byteOut() {
|
|
if e.b == 0xff {
|
|
e.rBlock()
|
|
return
|
|
}
|
|
|
|
if e.c < 0x8000000 {
|
|
e.lBlock()
|
|
return
|
|
}
|
|
e.b++
|
|
|
|
if e.b != 0xff {
|
|
e.lBlock()
|
|
return
|
|
}
|
|
e.c &= 0x7ffffff
|
|
e.rBlock()
|
|
}
|
|
|
|
// code0 as defined in Figure E.5.
|
|
func (e *Encoder) code0(ctx *codingContext, ctxNum uint32, qe uint16, i byte) {
|
|
if ctx.mps(ctxNum) == 0 {
|
|
e.codeMPS(ctx, ctxNum, qe, i)
|
|
} else {
|
|
e.codeLPS(ctx, ctxNum, qe, i)
|
|
}
|
|
}
|
|
|
|
// code1 as defined in Figure E.4.
|
|
func (e *Encoder) code1(ctx *codingContext, ctxNum uint32, qe uint16, i byte) {
|
|
if ctx.mps(ctxNum) == 1 {
|
|
e.codeMPS(ctx, ctxNum, qe, i)
|
|
} else {
|
|
e.codeLPS(ctx, ctxNum, qe, i)
|
|
}
|
|
}
|
|
|
|
// codeMPS as defined in Figure E.7.
|
|
func (e *Encoder) codeMPS(ctx *codingContext, ctxNum uint32, qe uint16, i byte) {
|
|
e.a -= qe
|
|
|
|
if e.a&0x8000 != 0 {
|
|
e.c += uint32(qe)
|
|
return
|
|
}
|
|
|
|
if e.a < qe {
|
|
e.a = qe
|
|
} else {
|
|
e.c += uint32(qe)
|
|
}
|
|
ctx.context[ctxNum] = stateTable[i].mps
|
|
e.renormalize()
|
|
}
|
|
|
|
// codeLPS as defined in Figure E.6.
|
|
func (e *Encoder) codeLPS(ctx *codingContext, ctxNum uint32, qe uint16, i byte) {
|
|
e.a -= qe
|
|
if e.a < qe {
|
|
e.c += uint32(qe)
|
|
} else {
|
|
e.a = qe
|
|
}
|
|
if stateTable[i].swtc == 1 {
|
|
ctx.flipMps(ctxNum)
|
|
}
|
|
ctx.context[ctxNum] = stateTable[i].lps
|
|
e.renormalize()
|
|
}
|
|
|
|
// returns the number of bytes of output in the given context
|
|
func (e *Encoder) dataSize() int {
|
|
return outputBufferSize*len(e.outputChunks) + e.outbufUsed
|
|
}
|
|
|
|
// emit a byte from the compressor by appending to the current
|
|
// output buffer. If the buffer is full, allocate a new one.
|
|
func (e *Encoder) emit() {
|
|
if e.outbufUsed == outputBufferSize {
|
|
e.outputChunks = append(e.outputChunks, e.outbuf)
|
|
e.outbuf = make([]byte, outputBufferSize)
|
|
e.outbufUsed = 0
|
|
}
|
|
|
|
e.outbuf[e.outbufUsed] = e.b
|
|
e.outbufUsed++
|
|
}
|
|
|
|
func (e *Encoder) encodeBit(ctx *codingContext, ctxNum uint32, d uint8) error {
|
|
const processName = "Encoder.encodeBit"
|
|
e.ec++
|
|
|
|
if ctxNum >= uint32(len(ctx.context)) {
|
|
return errors.Errorf(processName, "arithmetic encoder - invalid ctx number: '%d'", ctxNum)
|
|
}
|
|
|
|
i := ctx.context[ctxNum]
|
|
mps := ctx.mps(ctxNum)
|
|
qe := stateTable[i].qe
|
|
common.Log.Trace("EC: %d\t D: %d\t I: %d\t MPS: %d\t QE: %04X\t A: %04X\t C: %08X\t CT: %d\t B: %02X\t BP: %d", e.ec, d, i, mps, qe, e.a, e.c, e.ct, e.b, e.bp)
|
|
|
|
if d == 0 {
|
|
e.code0(ctx, ctxNum, qe, i)
|
|
} else {
|
|
e.code1(ctx, ctxNum, qe, i)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Encoder) encodeInteger(proc Class, value int) error {
|
|
const processName = "Encoder.encodeInteger"
|
|
if value > 2000000000 || value < -2000000000 {
|
|
return errors.Errorf(processName, "arithmetic encoder - invalid integer value: '%d'", value)
|
|
}
|
|
|
|
ctx := e.intCtx[proc]
|
|
prev := uint32(1)
|
|
var i int
|
|
|
|
for ; ; i++ {
|
|
if intEncRange[i].bot <= value && intEncRange[i].top >= value {
|
|
break
|
|
}
|
|
}
|
|
if value < 0 {
|
|
value = -value
|
|
}
|
|
value -= int(intEncRange[i].delta)
|
|
|
|
data := intEncRange[i].data
|
|
for j := uint8(0); j < intEncRange[i].bits; j++ {
|
|
v := data & 1
|
|
if err := e.encodeBit(ctx, prev, v); err != nil {
|
|
return errors.Wrap(err, processName, "")
|
|
}
|
|
|
|
data >>= 1
|
|
if prev&0x100 > 0 {
|
|
prev = (((prev << 1) | uint32(v)) & 0x1ff) | 0x100
|
|
} else {
|
|
prev = (prev << 1) | uint32(v)
|
|
}
|
|
}
|
|
|
|
// move the data value
|
|
value <<= 32 - intEncRange[i].intBits
|
|
for j := uint8(0); j < intEncRange[i].intBits; j++ {
|
|
v := uint8((uint32(value) & 0x80000000) >> 31)
|
|
if err := e.encodeBit(ctx, prev, v); err != nil {
|
|
return errors.Wrap(err, processName, "move data to the top of word")
|
|
}
|
|
value <<= 1
|
|
if prev&0x100 != 0 {
|
|
prev = (((prev << 1) | uint32(v)) & 0x1ff) | 0x100
|
|
} else {
|
|
prev = (prev << 1) | uint32(v)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Encoder) encodeIAID(symCodeLen, value int) error {
|
|
if e.iaidCtx == nil {
|
|
e.iaidCtx = newContext(1 << uint(symCodeLen))
|
|
}
|
|
|
|
mask := uint32(1<<uint32(symCodeLen+1)) - 1
|
|
value <<= uint(32 - symCodeLen)
|
|
prev := uint32(1)
|
|
|
|
for i := 0; i < symCodeLen; i++ {
|
|
tVal := prev & mask
|
|
v := uint8((uint32(value) & 0x80000000) >> 31)
|
|
|
|
if err := e.encodeBit(e.iaidCtx, tVal, v); err != nil {
|
|
return err
|
|
}
|
|
prev = (prev << 1) | uint32(v)
|
|
value <<= 1
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Encoder) encodeOOB(proc Class) error {
|
|
ctx := e.intCtx[proc]
|
|
|
|
err := e.encodeBit(ctx, 1, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = e.encodeBit(ctx, 3, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = e.encodeBit(ctx, 6, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = e.encodeBit(ctx, 12, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *Encoder) flush() {
|
|
e.setBits()
|
|
|
|
e.c <<= e.ct
|
|
e.byteOut()
|
|
|
|
e.c <<= e.ct
|
|
e.byteOut()
|
|
e.emit()
|
|
|
|
if e.b != 0xff {
|
|
e.bp++
|
|
e.b = 0xff
|
|
e.emit()
|
|
}
|
|
|
|
e.bp++
|
|
e.b = 0xac
|
|
e.bp++
|
|
e.emit()
|
|
}
|
|
|
|
func (e *Encoder) lBlock() {
|
|
if e.bp >= 0 {
|
|
e.emit()
|
|
}
|
|
e.bp++
|
|
e.b = uint8(e.c >> 19)
|
|
e.c &= 0x7ffff
|
|
e.ct = 8
|
|
}
|
|
|
|
func (e *Encoder) rBlock() {
|
|
if e.bp >= 0 {
|
|
e.emit()
|
|
}
|
|
e.bp++
|
|
e.b = uint8(e.c >> 20)
|
|
e.c &= 0xfffff
|
|
e.ct = 7
|
|
}
|
|
|
|
func (e *Encoder) renormalize() {
|
|
for {
|
|
e.a <<= 1
|
|
e.c <<= 1
|
|
e.ct--
|
|
if e.ct == 0 {
|
|
e.byteOut()
|
|
}
|
|
if (e.a & 0x8000) != 0 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *Encoder) setBits() {
|
|
tempC := e.c + uint32(e.a)
|
|
e.c |= 0xffff
|
|
if e.c >= tempC {
|
|
e.c -= 0x8000
|
|
}
|
|
}
|