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

* Fixing platform indepenedent integer size * Cleared test logs. * Cleared unnecessary int32 * Defined precise integer size for jbig2 segments.
426 lines
9.4 KiB
Go
426 lines
9.4 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 segments
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/bitmap"
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/reader"
|
|
)
|
|
|
|
// HalftoneRegion is the model for the jbig2 halftone region segment implementation - 7.4.5.1.
|
|
type HalftoneRegion struct {
|
|
r reader.StreamReader
|
|
h *Header
|
|
DataHeaderOffset int64
|
|
DataHeaderLength int64
|
|
DataOffset int64
|
|
DataLength int64
|
|
|
|
// Region segment information field, 7.4.1.
|
|
RegionSegment *RegionSegment
|
|
|
|
// Halftone segment information field, 7.4.5.1.1.
|
|
HDefaultPixel int8
|
|
CombinationOperator bitmap.CombinationOperator
|
|
HSkipEnabled bool
|
|
HTemplate byte
|
|
IsMMREncoded bool
|
|
|
|
// Halftone grid position and size, 7.4.5.1.2
|
|
// Width of the gray-scale image, 7.4.5.1.2.1
|
|
HGridWidth uint32
|
|
// Height of the gray-scale image, 7.4.5.1.2.2
|
|
HGridHeight uint32
|
|
// Horizontal offset of the grid, 7.4.5.1.2.3
|
|
HGridX int32
|
|
// Vertical offset of the grid, 7.4.5.1.2.4
|
|
HGridY int32
|
|
|
|
// Halftone grid vector, 7.4.5.1.3
|
|
// Horizontal coordinate of the halftone grid vector, 7.4.5.1.3.1
|
|
HRegionX uint16
|
|
// Vertical coordinate of the halftone grod vector, 7.4.5.1.3.2
|
|
HRegionY uint16
|
|
|
|
// Decoded data
|
|
HalftoneRegionBitmap *bitmap.Bitmap
|
|
|
|
// Previously decoded data from other regions or dictionaries, stored to use as patterns in this region.
|
|
Patterns []*bitmap.Bitmap
|
|
}
|
|
|
|
// Init implements Segmenter interface.
|
|
func (h *HalftoneRegion) Init(hd *Header, r reader.StreamReader) error {
|
|
h.r = r
|
|
h.h = hd
|
|
h.RegionSegment = NewRegionSegment(r)
|
|
return h.parseHeader()
|
|
}
|
|
|
|
// GetRegionBitmap implements Regioner interface.
|
|
func (h *HalftoneRegion) GetRegionBitmap() (*bitmap.Bitmap, error) {
|
|
if h.HalftoneRegionBitmap != nil {
|
|
return h.HalftoneRegionBitmap, nil
|
|
}
|
|
var err error
|
|
|
|
// 6.6.5 1)
|
|
h.HalftoneRegionBitmap = bitmap.New(int(h.RegionSegment.BitmapWidth), int(h.RegionSegment.BitmapHeight))
|
|
|
|
if h.Patterns == nil || len(h.Patterns) == 0 {
|
|
h.Patterns, err = h.GetPatterns()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if h.HDefaultPixel == 1 {
|
|
h.HalftoneRegionBitmap.SetDefaultPixel()
|
|
}
|
|
|
|
// 3)
|
|
bitsPerValueF := math.Ceil(math.Log(float64(len(h.Patterns))) / math.Log(2))
|
|
bitsPerValue := int(bitsPerValueF)
|
|
|
|
// 4)
|
|
var grayScaleValues [][]int
|
|
grayScaleValues, err = h.grayScaleDecoding(bitsPerValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = h.renderPattern(grayScaleValues); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return h.HalftoneRegionBitmap, nil
|
|
}
|
|
|
|
// GetRegionInfo implements Regioner interface.
|
|
func (h *HalftoneRegion) GetRegionInfo() *RegionSegment {
|
|
return h.RegionSegment
|
|
}
|
|
|
|
// GetPatterns gets the HalftoneRegion patterns.
|
|
func (h *HalftoneRegion) GetPatterns() ([]*bitmap.Bitmap, error) {
|
|
var (
|
|
patterns []*bitmap.Bitmap
|
|
err error
|
|
)
|
|
|
|
for _, s := range h.h.RTSegments {
|
|
var data Segmenter
|
|
data, err = s.GetSegmentData()
|
|
if err != nil {
|
|
common.Log.Debug("GetSegmentData failed: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
pattern, ok := data.(*PatternDictionary)
|
|
if !ok {
|
|
err = fmt.Errorf("related segment not a pattern dictionary: %T", data)
|
|
return nil, err
|
|
}
|
|
|
|
var tempPatterns []*bitmap.Bitmap
|
|
tempPatterns, err = pattern.GetDictionary()
|
|
if err != nil {
|
|
common.Log.Debug("pattern GetDictionary failed: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
patterns = append(patterns, tempPatterns...)
|
|
}
|
|
return patterns, nil
|
|
}
|
|
|
|
func (h *HalftoneRegion) checkInput() error {
|
|
if h.IsMMREncoded {
|
|
if h.HTemplate != 0 {
|
|
common.Log.Debug("HTemplate = %d should contain the value 0", h.HTemplate)
|
|
}
|
|
|
|
if h.HSkipEnabled {
|
|
common.Log.Debug("HSkipEnabled 0 %v (should contain the value false)", h.HSkipEnabled)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *HalftoneRegion) combineGrayscalePlanes(grayScalePlanes []*bitmap.Bitmap, j int) error {
|
|
byteIndex := 0
|
|
|
|
for y := 0; y < grayScalePlanes[j].Height; y++ {
|
|
for x := 0; x < grayScalePlanes[j].Width; x += 8 {
|
|
newValue, err := grayScalePlanes[j+1].GetByte(byteIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldValue, err := grayScalePlanes[j].GetByte(byteIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = grayScalePlanes[j].SetByte(byteIndex, bitmap.CombineBytes(oldValue, newValue, bitmap.CmbOpXor))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
byteIndex++
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *HalftoneRegion) computeGrayScalePlanes(grayScalePlanes []*bitmap.Bitmap, bitsPerValue int) ([][]int, error) {
|
|
grayScaleValues := make([][]int, h.HGridHeight)
|
|
|
|
for i := 0; i < len(grayScaleValues); i++ {
|
|
grayScaleValues[i] = make([]int, h.HGridWidth)
|
|
}
|
|
|
|
for y := 0; y < int(h.HGridHeight); y++ {
|
|
for x := 0; x < int(h.HGridWidth); x += 8 {
|
|
var minorWidth int
|
|
if d := int(h.HGridWidth) - x; d > 8 {
|
|
minorWidth = 8
|
|
} else {
|
|
minorWidth = d
|
|
}
|
|
|
|
byteIndex := grayScalePlanes[0].GetByteIndex(x, y)
|
|
|
|
for minorX := 0; minorX < minorWidth; minorX++ {
|
|
i := minorX + x
|
|
grayScaleValues[y][i] = 0
|
|
|
|
for j := 0; j < bitsPerValue; j++ {
|
|
bv, err := grayScalePlanes[j].GetByte(byteIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
shifted := (bv >> uint(7-i&7))
|
|
and1 := shifted & 1
|
|
multiplier := 1 << uint(j)
|
|
v := int(and1) * multiplier
|
|
grayScaleValues[y][i] += v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return grayScaleValues, nil
|
|
}
|
|
|
|
func (h *HalftoneRegion) computeSegmentDataStructure() error {
|
|
h.DataOffset = h.r.StreamPosition()
|
|
h.DataHeaderLength = h.DataOffset - h.DataHeaderOffset
|
|
h.DataLength = int64(h.r.Length()) - h.DataHeaderLength
|
|
return nil
|
|
}
|
|
|
|
func (h *HalftoneRegion) computeX(m, n int) int {
|
|
return h.shiftAndFill(int(h.HGridX) + m*int(h.HRegionY) + n*int(h.HRegionX))
|
|
}
|
|
|
|
func (h *HalftoneRegion) computeY(m, n int) int {
|
|
return h.shiftAndFill(int(h.HGridY) + m*int(h.HRegionX) - n*int(h.HRegionY))
|
|
}
|
|
|
|
func (h *HalftoneRegion) grayScaleDecoding(bitsPerValue int) ([][]int, error) {
|
|
var (
|
|
gbAtX []int8
|
|
gbAtY []int8
|
|
)
|
|
|
|
if !h.IsMMREncoded {
|
|
gbAtX = make([]int8, 4)
|
|
gbAtY = make([]int8, 4)
|
|
|
|
if h.HTemplate <= 1 {
|
|
gbAtX[0] = 3
|
|
} else if h.HTemplate >= 2 {
|
|
gbAtX[0] = 2
|
|
}
|
|
gbAtY[0] = -1
|
|
gbAtX[1] = -3
|
|
gbAtY[1] = -1
|
|
gbAtX[2] = 2
|
|
gbAtY[2] = -2
|
|
gbAtX[3] = -2
|
|
gbAtY[3] = -2
|
|
}
|
|
|
|
grayScalePlanes := make([]*bitmap.Bitmap, bitsPerValue)
|
|
|
|
// 1)
|
|
genericRegion := NewGenericRegion(h.r)
|
|
genericRegion.setParametersMMR(h.IsMMREncoded, h.DataOffset, h.DataLength, h.HGridHeight, h.HGridWidth, h.HTemplate, false, h.HSkipEnabled, gbAtX, gbAtY)
|
|
|
|
// 2)
|
|
j := bitsPerValue - 1
|
|
|
|
var err error
|
|
grayScalePlanes[j], err = genericRegion.GetRegionBitmap()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for j > 0 {
|
|
j--
|
|
genericRegion.Bitmap = nil
|
|
grayScalePlanes[j], err = genericRegion.GetRegionBitmap()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = h.combineGrayscalePlanes(grayScalePlanes, j); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return h.computeGrayScalePlanes(grayScalePlanes, bitsPerValue)
|
|
}
|
|
|
|
func (h *HalftoneRegion) parseHeader() error {
|
|
if err := h.RegionSegment.parseHeader(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Bit 7
|
|
b, err := h.r.ReadBit()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HDefaultPixel = int8(b)
|
|
|
|
// Bit 4-6
|
|
temp, err := h.r.ReadBits(3)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
h.CombinationOperator = bitmap.CombinationOperator(temp & 0xf)
|
|
|
|
// Bit 3
|
|
b, err = h.r.ReadBit()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if b == 1 {
|
|
h.HSkipEnabled = true
|
|
}
|
|
|
|
// Bit 1 - 2
|
|
temp, err = h.r.ReadBits(2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HTemplate = byte(temp & 0xf)
|
|
|
|
// Bit 0
|
|
b, err = h.r.ReadBit()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if b == 1 {
|
|
h.IsMMREncoded = true
|
|
}
|
|
|
|
temp, err = h.r.ReadBits(32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HGridWidth = uint32(temp & math.MaxUint32)
|
|
|
|
temp, err = h.r.ReadBits(32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HGridHeight = uint32(temp & math.MaxUint32)
|
|
|
|
temp, err = h.r.ReadBits(32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HGridX = int32(temp & math.MaxInt32)
|
|
|
|
temp, err = h.r.ReadBits(32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HGridY = int32(temp & math.MaxInt32)
|
|
|
|
temp, err = h.r.ReadBits(16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HRegionX = uint16(temp & math.MaxUint16)
|
|
|
|
temp, err = h.r.ReadBits(16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.HRegionY = uint16(temp & math.MaxUint16)
|
|
|
|
if err = h.computeSegmentDataStructure(); err != nil {
|
|
return err
|
|
}
|
|
return h.checkInput()
|
|
}
|
|
|
|
// renderPattern draws the pattern into the region bitmap, as described in 6.6.5.2.
|
|
func (h *HalftoneRegion) renderPattern(grayScaleValues [][]int) (err error) {
|
|
var x, y int
|
|
|
|
for m := 0; m < int(h.HGridHeight); m++ {
|
|
for n := 0; n < int(h.HGridWidth); n++ {
|
|
x = h.computeX(m, n)
|
|
y = h.computeY(m, n)
|
|
patternBitmap := h.Patterns[grayScaleValues[m][n]]
|
|
|
|
if err = bitmap.Blit(
|
|
patternBitmap, h.HalftoneRegionBitmap,
|
|
x+int(h.HGridX), y+int(h.HGridY), h.CombinationOperator,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func newHalftoneRegion(r *reader.Reader) *HalftoneRegion {
|
|
return &HalftoneRegion{r: r, RegionSegment: NewRegionSegment(r)}
|
|
}
|
|
|
|
func findMSB(n int) int {
|
|
if n == 0 {
|
|
return 0
|
|
}
|
|
n |= n >> 1
|
|
n |= n >> 2
|
|
n |= n >> 4
|
|
n |= n >> 8
|
|
n |= n >> 16
|
|
return (n + 1) >> 1
|
|
}
|
|
|
|
func (h *HalftoneRegion) shiftAndFill(value int) int {
|
|
value >>= 8
|
|
if value < 0 {
|
|
bitPosition := int(math.Log(float64(findMSB(value))) / math.Log(2))
|
|
l := 31 - bitPosition
|
|
for i := 1; i < l; i++ {
|
|
value |= (1 << uint(31-i))
|
|
}
|
|
}
|
|
return value
|
|
}
|