unipdf/internal/jbig2/bitmap/bin-reduce.go
Jacek Kucharczyk c582323a8f
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 11:47:41 +00:00

361 lines
10 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 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
}