unipdf/internal/jbig2/bitmap/bin-reduce.go

361 lines
10 KiB
Go
Raw Normal View History

JBIG2 Generic Encoder (#264) * Prepared skeleton and basic component implementations for the jbig2 encoding. * Added Bitset. Implemented Bitmap. * Decoder with old Arithmetic Decoder * Partly working arithmetic * Working arithmetic decoder. * MMR patched. * rebuild to apache. * Working generic * Working generic * Decoded full document * Update Jenkinsfile go version [master] (#398) * Update Jenkinsfile go version * Decoded AnnexH document * Minor issues fixed. * Update README.md * Fixed generic region errors. Added benchmark. Added bitmap unpadder. Added Bitmap toImage method. * Fixed endofpage error * Added integration test. * Decoded all test files without errors. Implemented JBIG2Global. * Merged with v3 version * Fixed the EOF in the globals issue * Fixed the JBIG2 ChocolateData Decode * JBIG2 Added license information * Minor fix in jbig2 encoding. * Applied the logging convention * Cleaned unnecessary imports * Go modules clear unused imports * checked out the README.md * Moved trace to Debug. Fixed the build integrate tag in the document_decode_test.go * Initial encoder skeleton * Applied UniPDF Developer Guide. Fixed lint issues. * Cleared documentation, fixed style issues. * Added jbig2 doc.go files. Applied unipdf guide style. * Minor code style changes. * Minor naming and style issues fixes. * Minor naming changes. Style issues fixed. * Review r11 fixes. * Added JBIG2 Encoder skeleton. * Moved Document and Page to jbig2/document package. Created decoder package responsible for decoding jbig2 stream. * Implemented raster functions. * Added raster uni low test funcitons. * Added raster low test functions * untracked files on jbig2-encoder: c869089 Added raster low test functions * index on jbig2-encoder: c869089 Added raster low test functions * Added morph files. * implemented jbig2 encoder basics * JBIG2 Encoder - Generic method * Added jbig2 image encode ttests, black/white image tests * cleaned and tested jbig2 package * unfinished jbig2 classified encoder * jbig2 minor style changes * minor jbig2 encoder changes * prepared JBIG2 Encoder * Style and lint fixes * Minor changes and lints * Fixed shift unsinged value build errors * Minor naming change * Added jbig2 encode, image gondels. Fixed jbig2 decode bug. * Provided jbig2 core.DecodeGlobals function. * Fixed JBIG2Encoder `r6` revision issues. * Removed public JBIG2Encoder document. * Minor style changes * added NewJBIG2Encoder function. * fixed JBIG2Encoder 'r9' revision issues. * Cleared 'r9' commented code. * Updated ACKNOWLEDGEMENETS. Fixed JBIG2Encoder 'r10' revision issues. Co-authored-by: Gunnsteinn Hall <gunnsteinn.hall@gmail.com>
2020-03-27 12:47:41 +01:00
/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/
package bitmap
import (
"encoding/binary"
"github.com/unidoc/unipdf/v3/common"
"github.com/unidoc/unipdf/v3/internal/jbig2/errors"
)
// reduceBinaryCascade performs up to four cascades 2x rank reductions.
func reduceRankBinaryCascade(s *Bitmap, levels ...int) (d *Bitmap, err error) {
const processName = "reduceRankBinaryCascade"
if s == nil {
return nil, errors.Error(processName, "source bitmap not defined")
}
if len(levels) == 0 || len(levels) > 4 {
return nil, errors.Error(processName, "there must be at least one and at most 4 levels")
}
if levels[0] <= 0 {
common.Log.Debug("level1 <= 0 - no reduction")
d, err = copyBitmap(nil, s)
if err != nil {
return nil, errors.Wrap(err, processName, "level1 <= 0")
}
return d, nil
}
tab := makeSubsampleTab2x()
d = s // no need to copy the 's' as the 'reduceRankBinary2 creates new 'd' Bitmap.
for i, level := range levels {
if level <= 0 {
break
}
d, err = reduceRankBinary2(d, level, tab)
if err != nil {
return nil, errors.Wrapf(err, processName, "level%d reduction", i)
}
}
return d, nil
}
// reduceRankBinary2 reduces the size of the 'source' bitmap by 2.
// The 'level' is the rank threshold which defines the number of pixels in each 2x2 region
// that are required to set corresponding pixel 'ON' int the 'd' Bitmap.
func reduceRankBinary2(s *Bitmap, level int, tab []byte) (d *Bitmap, err error) {
const processName = "reduceRankBinary2"
if s == nil {
return nil, errors.Error(processName, "source bitmap not defined")
}
if level < 1 || level > 4 {
return nil, errors.Error(processName, "level must be in set {1,2,3,4}")
}
if s.Height <= 1 {
return nil, errors.Errorf(processName, "source height must be at least '2' - is: '%d'", s.Height)
}
// create a bitmap half a side of the 's' bitmap
d = New(s.Width/2, s.Height/2)
if tab == nil {
tab = makeSubsampleTab2x()
}
// get the data in the []uint32 word form
// let's assume big endian
// wpl := (s.Width + 31) >> 5
wpl := min(s.RowStride, 2*d.RowStride)
switch level {
case 1:
err = reduceRankBinary2Level1(s, d, level, tab, wpl)
case 2:
err = reduceRankBinary2Level2(s, d, level, tab, wpl)
case 3:
err = reduceRankBinary2Level3(s, d, level, tab, wpl)
case 4:
err = reduceRankBinary2Level4(s, d, level, tab, wpl)
}
if err != nil {
return nil, err
}
return d, nil
}
func reduceRankBinary2Level1(s, d *Bitmap, level int, tab []byte, wpl int) (err error) {
const processName = "reduceRankBinary2Level1"
var (
lineS, lineD, i, id, j, jd, k, index int
word1, word2 uint32
byte0, byte1 byte
twoBytes uint16
)
bytes1 := make([]byte, 4)
bytes2 := make([]byte, 4)
for i = 0; i < s.Height-1; i, id = i+2, id+1 {
lineS = i * s.RowStride
lineD = id * d.RowStride
for j, jd = 0, 0; j < wpl; j, jd = j+4, jd+1 {
// get four bytes from the bitmap 's' and 'd' at the j'th index
for k = 0; k < 4; k++ {
index = lineS + j + k
// check if the bitmap 's' has byte at 'index'
if index <= len(s.Data)-1 && index < lineS+s.RowStride {
bytes1[k] = s.Data[index]
} else {
bytes1[k] = 0x00
}
index = lineS + s.RowStride + j + k
if index <= len(s.Data)-1 && index < lineS+(2*s.RowStride) {
bytes2[k] = s.Data[index]
} else {
bytes2[k] = 0x00
}
}
word1 = binary.BigEndian.Uint32(bytes1)
word2 = binary.BigEndian.Uint32(bytes2)
word2 |= word1
word2 |= word2 << 1
word2 &= 0xaaaaaaaa
word1 = word2 | (word2 << 7)
byte0 = byte(word1 >> 24)
byte1 = byte((word1 >> 8) & 0xff)
index = lineD + jd
if index+1 == len(d.Data)-1 || index+1 >= lineD+d.RowStride {
// set only one byte0 if there is no place for 'two' bytes.
d.Data[index] = tab[byte0]
} else {
twoBytes = (uint16(tab[byte0]) << 8) | uint16(tab[byte1])
if err = d.setTwoBytes(index, twoBytes); err != nil {
return errors.Wrapf(err, processName, "setting two bytes failed, index: %d", index)
}
// while setting two bytes the index should increase.
jd++
}
}
}
return nil
}
func reduceRankBinary2Level2(s, d *Bitmap, level int, tab []byte, wpl int) (err error) {
const processName = "reduceRankBinary2Level2"
var (
lineS, lineD, i, id, j, jd, k, index int
word1, word2, word3, word4 uint32
byte0, byte1 byte
twoBytes uint16
)
bytes1 := make([]byte, 4)
bytes2 := make([]byte, 4)
for i = 0; i < s.Height-1; i, id = i+2, id+1 {
lineS = i * s.RowStride
lineD = id * d.RowStride
for j, jd = 0, 0; j < wpl; j, jd = j+4, jd+1 {
// get four bytes from the bitmap 's' and 'd' at the j'th index
for k = 0; k < 4; k++ {
index = lineS + j + k
// check if the bitmap 's' has byte at 'index'
if index <= len(s.Data)-1 && index < lineS+s.RowStride {
bytes1[k] = s.Data[index]
} else {
bytes1[k] = 0x00
}
index = lineS + s.RowStride + j + k
if index <= len(s.Data)-1 && index < lineS+(2*s.RowStride) {
bytes2[k] = s.Data[index]
} else {
bytes2[k] = 0x00
}
}
word1 = binary.BigEndian.Uint32(bytes1)
word2 = binary.BigEndian.Uint32(bytes2)
word3 = word1 & word2
word3 |= word3 << 1
word4 = word1 | word2
word4 &= word4 << 1
word2 = word3 | word4
word2 &= 0xaaaaaaaa
word1 = word2 | (word2 << 7)
byte0 = byte(word1 >> 24)
byte1 = byte((word1 >> 8) & 0xff)
index = lineD + jd
if index+1 == len(d.Data)-1 || index+1 >= lineD+d.RowStride {
// set only one byte0 if there is no place for 'two' bytes.
if err = d.SetByte(index, tab[byte0]); err != nil {
return errors.Wrapf(err, processName, "index: %d", index)
}
} else {
twoBytes = (uint16(tab[byte0]) << 8) | uint16(tab[byte1])
if err = d.setTwoBytes(index, twoBytes); err != nil {
return errors.Wrapf(err, processName, "setting two bytes failed, index: %d", index)
}
// while setting two bytes the index should increase.
jd++
}
}
}
return nil
}
func reduceRankBinary2Level3(s, d *Bitmap, level int, tab []byte, wpl int) (err error) {
const processName = "reduceRankBinary2Level3"
var (
lineS, lineD, i, id, j, jd, k, index int
word1, word2, word3, word4 uint32
byte0, byte1 byte
twoBytes uint16
)
bytes1 := make([]byte, 4)
bytes2 := make([]byte, 4)
for i = 0; i < s.Height-1; i, id = i+2, id+1 {
lineS = i * s.RowStride
lineD = id * d.RowStride
for j, jd = 0, 0; j < wpl; j, jd = j+4, jd+1 {
// get four bytes from the bitmap 's' and 'd' at the j'th index
for k = 0; k < 4; k++ {
index = lineS + j + k
// check if the bitmap 's' has byte at 'index'
if index <= len(s.Data)-1 && index < lineS+s.RowStride {
bytes1[k] = s.Data[index]
} else {
bytes1[k] = 0x00
}
index = lineS + s.RowStride + j + k
if index <= len(s.Data)-1 && index < lineS+(2*s.RowStride) {
bytes2[k] = s.Data[index]
} else {
bytes2[k] = 0x00
}
}
word1 = binary.BigEndian.Uint32(bytes1)
word2 = binary.BigEndian.Uint32(bytes2)
word3 = word1 & word2
word3 |= word3 << 1
word4 = word1 | word2
word4 &= word4 << 1
word2 = word3 & word4
word2 &= 0xaaaaaaaa
word1 = word2 | (word2 << 7)
byte0 = byte(word1 >> 24)
byte1 = byte((word1 >> 8) & 0xff)
index = lineD + jd
if index+1 == len(d.Data)-1 || index+1 >= lineD+d.RowStride {
// set only one byte0 if there is no place for 'two' bytes.
if err = d.SetByte(index, tab[byte0]); err != nil {
return errors.Wrapf(err, processName, "index: %d", index)
}
} else {
twoBytes = (uint16(tab[byte0]) << 8) | uint16(tab[byte1])
if err = d.setTwoBytes(index, twoBytes); err != nil {
return errors.Wrapf(err, processName, "setting two bytes failed, index: %d", index)
}
// while setting two bytes the index should increase.
jd++
}
}
}
return nil
}
func reduceRankBinary2Level4(s, d *Bitmap, level int, tab []byte, wpl int) (err error) {
const processName = "reduceRankBinary2Level4"
var (
lineS, lineD, i, id, j, jd, k, index int
word1, word2 uint32
byte0, byte1 byte
twoBytes uint16
)
bytes1 := make([]byte, 4)
bytes2 := make([]byte, 4)
for i = 0; i < s.Height-1; i, id = i+2, id+1 {
lineS = i * s.RowStride
lineD = id * d.RowStride
for j, jd = 0, 0; j < wpl; j, jd = j+4, jd+1 {
// get four bytes from the bitmap 's' and 'd' at the j'th index
for k = 0; k < 4; k++ {
index = lineS + j + k
// check if the bitmap 's' has byte at 'index'
if index <= len(s.Data)-1 && index < lineS+s.RowStride {
bytes1[k] = s.Data[index]
} else {
bytes1[k] = 0x00
}
index = lineS + s.RowStride + j + k
if index <= len(s.Data)-1 && index < lineS+(2*s.RowStride) {
bytes2[k] = s.Data[index]
} else {
bytes2[k] = 0x00
}
}
word1 = binary.BigEndian.Uint32(bytes1)
word2 = binary.BigEndian.Uint32(bytes2)
word2 &= word1
word2 &= word2 << 1
word2 &= 0xaaaaaaaa
word1 = word2 | (word2 << 7)
byte0 = byte(word1 >> 24)
byte1 = byte((word1 >> 8) & 0xff)
index = lineD + jd
if index+1 == len(d.Data)-1 || index+1 >= lineD+d.RowStride {
// set only one byte0 if there is no place for 'two' bytes.
d.Data[index] = tab[byte0]
if err = d.SetByte(index, tab[byte0]); err != nil {
return errors.Wrapf(err, processName, "index: %d", index)
}
} else {
twoBytes = (uint16(tab[byte0]) << 8) | uint16(tab[byte1])
if err = d.setTwoBytes(index, twoBytes); err != nil {
return errors.Wrapf(err, processName, "setting two bytes failed, index: %d", index)
}
// while setting two bytes the index should increase.
jd++
}
}
}
return nil
}
func makeSubsampleTab2x() (tab []byte) {
tab = make([]byte, 256)
for i := 0; i < 256; i++ {
i := byte(i)
tab[i] = (i & 0x01) | /* 7 */
((i & 0x04) >> 1) | /* 6 */
((i & 0x10) >> 2) | /* 5 */
((i & 0x40) >> 3) | /* 4 */
((i & 0x02) << 3) | /* 3 */
((i & 0x08) << 2) | /* 2 */
((i & 0x20) << 1) | /* 1 */
(i & 0x80) /* 0 */
}
return tab
}