Jacek Kucharczyk e85616cec2 JBIG2Decoder implementation (#67)
* 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
* Decoded full document
* 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
* 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.
* Integrate jbig2 tests with build system
* Added jbig2 integration test golden files.
* Minor jbig2 integration test fix
* Removed jbig2 integration image assertions
* Fixed jbig2 rowstride issue. Implemented jbig2 bit writer
* Changed golden files logic. Fixes r13 issues.
2019-07-14 21:18:40 +00:00

253 lines
5.8 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 (
"errors"
"image"
"image/color"
"github.com/unidoc/unipdf/v3/common"
"github.com/unidoc/unipdf/v3/internal/jbig2/writer"
)
// ErrIndexOutOfRange is the error that returns if the bitmap byte index is out of range.
var ErrIndexOutOfRange = errors.New("bitmap byte index out of range")
// Bitmap is the jbig2 bitmap representation.
type Bitmap struct {
// Width and Height represents bitmap dimensions.
Width, Height int
// BitmapNumber is the bitmap's id number.
BitmapNumber int
// RowStride is the number of bytes set per row.
RowStride int
// Data saves the bits data for the bitmap.
Data []byte
isVanilla bool
}
// New creates new bitmap with the parameters as provided in the arguments.
func New(width, height int) *Bitmap {
bm := &Bitmap{
Width: width,
Height: height,
RowStride: (width + 7) >> 3,
}
bm.Data = make([]byte, height*bm.RowStride)
return bm
}
// NewWithData creates new bitmap with the provided 'width', 'height' and the byte slice 'data'.
func NewWithData(width, height int, data []byte) *Bitmap {
bm := New(width, height)
bm.Data = data
return bm
}
// Equals checks if all the pixels in the 'b' bitmap are equals to the 's' bitmap.
func (b *Bitmap) Equals(s *Bitmap) bool {
if len(b.Data) != len(s.Data) {
return false
}
for y := 0; y < b.Height; y++ {
for x := 0; x < b.Width; x++ {
if b.GetPixel(x, y) != s.GetPixel(x, y) {
return false
}
}
}
return true
}
// GetBitOffset gets the bit offset at the 'x' coordinate.
func (b *Bitmap) GetBitOffset(x int) int {
return x & 0x07
}
// GetByte gets and returns the byte at given 'index'.
func (b *Bitmap) GetByte(index int) (byte, error) {
if index > len(b.Data)-1 || index < 0 {
return 0, ErrIndexOutOfRange
}
return b.Data[index], nil
}
// GetByteIndex gets the byte index from the bitmap at coordinates 'x','y'.
func (b *Bitmap) GetByteIndex(x, y int) int {
return y*b.RowStride + (x >> 3)
}
// GetChocolateData gets bitmap data as a byte slice with Chocolate bit interpretation.
// 'Chocolate' data is the bit interpretation where the 0'th bit means white and the 1'th bit means black.
// The naming convention based on the: `https://en.wikipedia.org/wiki/Binary_image#Interpretation` page.
func (b *Bitmap) GetChocolateData() []byte {
if b.isVanilla {
b.inverseData()
}
return b.Data
}
// GetPixel gets the pixel value at the coordinates 'x', 'y'.
func (b *Bitmap) GetPixel(x, y int) bool {
i := b.GetByteIndex(x, y)
o := b.GetBitOffset(x)
shift := uint(7 - o)
if i > len(b.Data)-1 {
common.Log.Debug("Trying to get pixel out of the data range. x: '%d', y:'%d', bm: '%s'", x, y, b)
return false
}
if (b.Data[i]>>shift)&0x01 >= 1 {
return true
}
return false
}
// GetUnpaddedData gets the data without row stride padding.
// The unpadded data contains bitmap.Height * bitmap.Width bits with
// optional last byte padding.
func (b *Bitmap) GetUnpaddedData() ([]byte, error) {
padding := uint(b.Width & 0x07)
if padding == 0 {
return b.Data, nil
}
size := b.Width * b.Height
if size%8 != 0 {
size >>= 3
size++
} else {
size >>= 3
}
data := make([]byte, size)
w := writer.NewMSB(data)
for y := 0; y < b.Height; y++ {
// btIndex is the byte index per row.
for btIndex := 0; btIndex < b.RowStride; btIndex++ {
bt := b.Data[y*b.RowStride+btIndex]
if btIndex != b.RowStride-1 {
err := w.WriteByte(bt)
if err != nil {
return nil, err
}
continue
}
for i := uint(0); i < padding; i++ {
err := w.WriteBit(int(bt >> (7 - i) & 0x01))
if err != nil {
return nil, err
}
}
}
}
return data, nil
}
// GetVanillaData gets bitmap data as a byte slice with Vanilla bit interpretation.
// 'Vanilla' is the bit interpretation where the 0'th bit means black and 1'th bit means white.
// The naming convention based on the `https://en.wikipedia.org/wiki/Binary_image#Interpretation` page.
func (b *Bitmap) GetVanillaData() []byte {
if !b.isVanilla {
b.inverseData()
}
return b.Data
}
// SetPixel sets the pixel at 'x', 'y' coordinates with the value of 'pixel'.
// Returns an error if the index is out of range.
func (b *Bitmap) SetPixel(x, y int, pixel byte) error {
i := b.GetByteIndex(x, y)
if i > len(b.Data)-1 {
return ErrIndexOutOfRange
}
o := b.GetBitOffset(x)
shift := uint(7 - o)
src := b.Data[i]
result := src | (pixel & 0x01 << shift)
b.Data[i] = result
return nil
}
// SetDefaultPixel sets all bits within bitmap to '1'.
func (b *Bitmap) SetDefaultPixel() {
for i := range b.Data {
b.Data[i] = byte(0xff)
}
}
// SetByte sets the byte at 'index' with value 'v'.
// Returns an error if the index is out of range.
func (b *Bitmap) SetByte(index int, v byte) error {
if index > len(b.Data)-1 || index < 0 {
return ErrIndexOutOfRange
}
b.Data[index] = v
return nil
}
// String implements the Stringer interface.
func (b *Bitmap) String() string {
var s = "\n"
for y := 0; y < b.Height; y++ {
var row string
for x := 0; x < b.Width; x++ {
pix := b.GetPixel(x, y)
if pix {
row += "1"
} else {
row += "0"
}
}
s += row + "\n"
}
return s
}
// ToImage gets the bitmap data and store in the image.Image.
func (b *Bitmap) ToImage() image.Image {
img := image.NewGray(image.Rect(0, 0, b.Width-1, b.Height-1))
for x := 0; x < b.Width; x++ {
for y := 0; y < b.Height; y++ {
c := color.Black
if b.GetPixel(x, y) {
c = color.White
}
img.Set(x, y, c)
}
}
return img
}
// InverseData inverses the data if the 'isChocolate' flag matches
// current bitmap 'isVanilla' state.
func (b *Bitmap) InverseData(isChocolate bool) {
if b.isVanilla != !isChocolate {
b.inverseData()
}
}
func (b *Bitmap) inverseData() {
for i := 0; i < len(b.Data); i++ {
b.Data[i] = ^b.Data[i]
}
b.isVanilla = !b.isVanilla
}