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 * 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>
243 lines
7.0 KiB
Go
243 lines
7.0 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 (
|
|
"sort"
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/basic"
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/errors"
|
|
)
|
|
|
|
// Point is the basic structure that contains x, y float32 values.
|
|
// In compare with image.Point the x and y are floats not integers.
|
|
type Point struct {
|
|
X, Y float32
|
|
}
|
|
|
|
// Points is the slice of the float Points that has panic safe methods for getting and adding new Points.
|
|
type Points []Point
|
|
|
|
// Add adds the points 'pt' to the slice of points.
|
|
func (p *Points) Add(pt *Points) error {
|
|
const processName = "Points.Add"
|
|
if p == nil {
|
|
return errors.Error(processName, "points not defined")
|
|
}
|
|
if pt == nil {
|
|
return errors.Error(processName, "argument points not defined")
|
|
}
|
|
*p = append(*p, *pt...)
|
|
return nil
|
|
}
|
|
|
|
// AddPoint adds the Point{'x', 'y'} to the 'p' Points.
|
|
func (p *Points) AddPoint(x, y float32) {
|
|
*p = append(*p, Point{x, y})
|
|
}
|
|
|
|
// Get gets the point at 'i' index.
|
|
// Returns error if the 'i' index is out of range.
|
|
func (p Points) Get(i int) (Point, error) {
|
|
if i > len(p)-1 {
|
|
return Point{}, errors.Errorf("Points.Get", "index: '%d' out of range", i)
|
|
}
|
|
return p[i], nil
|
|
}
|
|
|
|
// GetIntX gets integer value of x coordinate for the point at index 'i'.
|
|
func (p Points) GetIntX(i int) (int, error) {
|
|
if i >= len(p) {
|
|
return 0, errors.Errorf("Points.GetIntX", "index: '%d' out of range", i)
|
|
}
|
|
return int(p[i].X), nil
|
|
}
|
|
|
|
// GetIntY gets integer value of y coordinate for the point at index 'i'.
|
|
func (p Points) GetIntY(i int) (int, error) {
|
|
if i >= len(p) {
|
|
return 0, errors.Errorf("Points.GetIntY", "index: '%d' out of range", i)
|
|
}
|
|
return int(p[i].Y), nil
|
|
}
|
|
|
|
// GetGeometry gets the geometry 'x' and 'y' of the point at the 'i' index.
|
|
// Returns error if the index is out of range.
|
|
func (p Points) GetGeometry(i int) (x, y float32, err error) {
|
|
if i > len(p)-1 {
|
|
return 0, 0, errors.Errorf("Points.Get", "index: '%d' out of range", i)
|
|
}
|
|
pt := p[i]
|
|
return pt.X, pt.Y, nil
|
|
}
|
|
|
|
// Size returns the size of the points slice.
|
|
func (p Points) Size() int {
|
|
return len(p)
|
|
}
|
|
|
|
// XSorter is the sorter function based on the points 'x' coordinates.
|
|
func (p Points) XSorter() func(i, j int) bool {
|
|
return func(i, j int) bool {
|
|
return p[i].X < p[j].X
|
|
}
|
|
}
|
|
|
|
// YSorter is the sorter function based on the points 'y' coordinates.
|
|
func (p Points) YSorter() func(i, j int) bool {
|
|
return func(i, j int) bool {
|
|
return p[i].Y < p[j].Y
|
|
}
|
|
}
|
|
|
|
// ClassedPoints is the wrapper that contains both points and integer slice, where
|
|
// each index in point relates to the index of int slice.
|
|
// Even though the type implements sort.Interface, don't use it directly - as this would panic.
|
|
// Use SortByX, or SortByY function.
|
|
type ClassedPoints struct {
|
|
*Points
|
|
basic.IntSlice
|
|
currentSortFunction func(i, j int) bool
|
|
}
|
|
|
|
// NewClassedPoints creates and validates PointsBitmaps based on provided 'points'
|
|
// and 'classes'. The classes contains id's of the classes related to the index of the 'points'.
|
|
func NewClassedPoints(points *Points, classes basic.IntSlice) (*ClassedPoints, error) {
|
|
const processName = "NewClassedPoints"
|
|
if points == nil {
|
|
return nil, errors.Error(processName, "provided nil points")
|
|
}
|
|
if classes == nil {
|
|
return nil, errors.Error(processName, "provided nil classes")
|
|
}
|
|
// create ClassedPoints
|
|
c := &ClassedPoints{Points: points, IntSlice: classes}
|
|
// validate all the input classes.
|
|
if err := c.validateIntSlice(); err != nil {
|
|
return nil, errors.Wrap(err, processName, "")
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// GetIntXByClass gets the integer of point.Y by the index of the 'IntSlice'.
|
|
func (c *ClassedPoints) GetIntXByClass(i int) (int, error) {
|
|
const processName = "ClassedPoints.GetIntYByClass"
|
|
if i >= c.IntSlice.Size() {
|
|
return 0, errors.Errorf(processName, "i: '%d' is out of the range of the IntSlice", i)
|
|
}
|
|
return int(c.XAtIndex(i)), nil
|
|
}
|
|
|
|
// GetIntYByClass gets the integer of point.Y by the index of the 'IntSlice'.
|
|
func (c *ClassedPoints) GetIntYByClass(i int) (int, error) {
|
|
const processName = "ClassedPoints.GetIntYByClass"
|
|
if i >= c.IntSlice.Size() {
|
|
return 0, errors.Errorf(processName, "i: '%d' is out of the range of the IntSlice", i)
|
|
}
|
|
return int(c.YAtIndex(i)), nil
|
|
}
|
|
|
|
// GroupByY groups provided intSlice into ClassedPoints based on their 'Y'.
|
|
func (c *ClassedPoints) GroupByY() ([]*ClassedPoints, error) {
|
|
const processName = "ClassedPoints.GroupByY"
|
|
if err := c.validateIntSlice(); err != nil {
|
|
return nil, errors.Wrap(err, processName, "")
|
|
}
|
|
if c.IntSlice.Size() == 0 {
|
|
return nil, errors.Error(processName, "No classes provided")
|
|
}
|
|
// sort the classes by Y
|
|
c.SortByY()
|
|
// define the variables
|
|
var (
|
|
res []*ClassedPoints
|
|
tempY int
|
|
)
|
|
y := -1
|
|
var currentPoints *ClassedPoints
|
|
for i := 0; i < len(c.IntSlice); i++ {
|
|
tempY = int(c.YAtIndex(i))
|
|
if tempY != y {
|
|
currentPoints = &ClassedPoints{Points: c.Points}
|
|
y = tempY
|
|
res = append(res, currentPoints)
|
|
}
|
|
currentPoints.IntSlice = append(currentPoints.IntSlice, c.IntSlice[i])
|
|
}
|
|
// sort each layer of the points by the 'x'
|
|
for _, pts := range res {
|
|
pts.SortByX()
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// SortByY sorts classed points by y coordinate.
|
|
func (c *ClassedPoints) SortByY() {
|
|
c.currentSortFunction = c.ySortFunction()
|
|
sort.Sort(c)
|
|
}
|
|
|
|
// SortByX sorts classed points by x coordinate.
|
|
func (c *ClassedPoints) SortByX() {
|
|
c.currentSortFunction = c.xSortFunction()
|
|
sort.Sort(c)
|
|
}
|
|
|
|
// compile time check for the sort.Interface implementation.
|
|
var _ sort.Interface = &ClassedPoints{}
|
|
|
|
// Less implements sort.Interface.
|
|
func (c *ClassedPoints) Less(i, j int) bool {
|
|
return c.currentSortFunction(i, j)
|
|
}
|
|
|
|
// Swap implements sort.Interface interface.
|
|
func (c *ClassedPoints) Swap(i, j int) {
|
|
// swap only the slices of the indexes.
|
|
c.IntSlice[i], c.IntSlice[j] = c.IntSlice[j], c.IntSlice[i]
|
|
}
|
|
|
|
// Len implements sort.Interface interface.
|
|
func (c *ClassedPoints) Len() int {
|
|
return c.IntSlice.Size()
|
|
}
|
|
|
|
// XAtIndex gets the 'x' coordinate from the points where the 'i' is the index
|
|
// of the IntSlice.
|
|
func (c *ClassedPoints) XAtIndex(i int) float32 {
|
|
return (*c.Points)[c.IntSlice[i]].X
|
|
}
|
|
|
|
func (c *ClassedPoints) xSortFunction() func(i int, j int) bool {
|
|
return func(i, j int) bool {
|
|
return c.XAtIndex(i) < c.XAtIndex(j)
|
|
}
|
|
}
|
|
|
|
func (c *ClassedPoints) validateIntSlice() error {
|
|
const processName = "validateIntSlice"
|
|
// check if all classes are within the context of the 'points'.
|
|
for _, idx := range c.IntSlice {
|
|
// the index must be within the range of the points.
|
|
if idx >= (c.Points.Size()) {
|
|
return errors.Errorf(processName, "class id: '%d' is not a valid index in the points of size: %d", idx, c.Points.Size())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// YAtIndex gets the 'y' coordinate from the points where the 'i' is the index
|
|
// of the IntSlice.
|
|
func (c *ClassedPoints) YAtIndex(i int) float32 {
|
|
return (*c.Points)[c.IntSlice[i]].Y
|
|
}
|
|
|
|
func (c *ClassedPoints) ySortFunction() func(i int, j int) bool {
|
|
return func(i, j int) bool {
|
|
return c.YAtIndex(i) < c.YAtIndex(j)
|
|
}
|
|
}
|