mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00

* 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.
253 lines
5.8 KiB
Go
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
|
|
}
|