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

213 lines
6.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 (
"image"
"github.com/unidoc/unipdf/v3/internal/jbig2/basic"
"github.com/unidoc/unipdf/v3/internal/jbig2/errors"
)
// Boxes is the wrapper over the slice of image.Rectangles that allows to
// get and add the image.Rectangle safely.
type Boxes []*image.Rectangle
// Add adds the 'box' to the provided 'Boxes'.
func (b *Boxes) Add(box *image.Rectangle) error {
if b == nil {
return errors.Error("Boxes.Add", "'Boxes' not defined")
}
*b = append(*b, box)
return nil
}
// Get gets the box at 'i' index. Returns error if the index 'i' is out of range.
func (b *Boxes) Get(i int) (*image.Rectangle, error) {
const processName = "Boxes.Get"
if b == nil {
return nil, errors.Error(processName, "'Boxes' not defined")
}
if i > len(*b)-1 {
return nil, errors.Errorf(processName, "index: '%d' out of range", i)
}
return (*b)[i], nil
}
// SelectBySize select sthe boxes 'b' by provided 'width' 'height', location filter 'tp' and 'relation'.
// If nothing changes the function returns the boxes 'b' by itself.
func (b *Boxes) SelectBySize(width, height int, tp LocationFilter, relation SizeComparison) (result *Boxes, err error) {
const processName = "Boxes.SelectBySize"
if b == nil {
return nil, errors.Error(processName, "boxes 'b' not defined")
}
// return boxes 'b' if its' empty.
if len(*b) == 0 {
return b, nil
}
switch tp {
case LocSelectWidth, LocSelectHeight, LocSelectIfEither, LocSelectIfBoth:
default:
return nil, errors.Errorf(processName, "invalid filter type: %d", tp)
}
switch relation {
case SizeSelectIfLT, SizeSelectIfGT, SizeSelectIfLTE, SizeSelectIfGTE:
default:
return nil, errors.Errorf(processName, "invalid relation type: '%d'", tp)
}
// get the indexes of the boxes that matches given arguments.
na := b.makeSizeIndicator(width, height, tp, relation)
// select the boxes from the 'b' that matches indexes found in the 'na'.
d, err := b.selectWithIndicator(na)
if err != nil {
return nil, errors.Wrap(err, processName, "")
}
return d, nil
}
func (b *Boxes) makeSizeIndicator(width, height int, tp LocationFilter, relation SizeComparison) *basic.NumSlice {
na := &basic.NumSlice{}
var ival, w, h int
for _, box := range *b {
ival = 0
w, h = box.Dx(), box.Dy()
switch tp {
case LocSelectWidth:
if (relation == SizeSelectIfLT && w < width) ||
(relation == SizeSelectIfGT && w > width) ||
(relation == SizeSelectIfLTE && w <= width) ||
(relation == SizeSelectIfGTE && w >= width) {
ival = 1
}
case LocSelectHeight:
if (relation == SizeSelectIfLT && h < height) ||
(relation == SizeSelectIfGT && h > height) ||
(relation == SizeSelectIfLTE && h <= height) ||
(relation == SizeSelectIfGTE && h >= height) {
ival = 1
}
case LocSelectIfEither:
if (relation == SizeSelectIfLT && (h < height || w < width)) ||
(relation == SizeSelectIfGT && (h > height || w > width)) ||
(relation == SizeSelectIfLTE && (h <= height || w <= width)) ||
(relation == SizeSelectIfGTE && (h >= height || w >= width)) {
ival = 1
}
case LocSelectIfBoth:
if (relation == SizeSelectIfLT && (h < height && w < width)) ||
(relation == SizeSelectIfGT && (h > height && w > width)) ||
(relation == SizeSelectIfLTE && (h <= height && w <= width)) ||
(relation == SizeSelectIfGTE && (h >= height && w >= width)) {
ival = 1
}
}
na.AddInt(ival)
}
return na
}
// selectWithIndicator selects the boxes based on the 'na' NumSlice that has indexes of
// the boxes that should be selected.
func (b *Boxes) selectWithIndicator(na *basic.NumSlice) (d *Boxes, err error) {
const processName = "Boxes.selectWithIndicator"
if b == nil {
return nil, errors.Error(processName, "boxes 'b' not defined")
}
if na == nil {
return nil, errors.Error(processName, "'na' not defined")
}
if len(*na) != len(*b) {
return nil, errors.Error(processName, "boxes 'b' has different size than 'na'")
}
var ival, changeCount int
for i := 0; i < len(*na); i++ {
if ival, err = na.GetInt(i); err != nil {
return nil, errors.Wrap(err, processName, "checking count")
}
if ival == 1 {
changeCount++
}
}
if changeCount == len(*b) {
return b, nil
}
boxes := Boxes{}
for i := 0; i < len(*na); i++ {
// we don't have to use the 'GetInt' now - it was already checked on the first iteration.
ival = int((*na)[i])
if ival == 0 {
continue
}
// as in the 'na' we don't have to check if the boxes 'b' has the i'th box - it was already checked
// with the boxes size.
boxes = append(boxes, (*b)[i])
}
d = &boxes
return d, nil
}
// ClipBoxToRectangle clips the image.Rectangle 'box' for the provided wi, hi which are rectangle representing image.
// The UL corner of the 'box' is assumed to be at (0,0) and LR at ('wi' - 1, 'hi' - 1)
func ClipBoxToRectangle(box *image.Rectangle, wi, hi int) (out *image.Rectangle, err error) {
const processName = "ClipBoxToRectangle"
if box == nil {
return nil, errors.Error(processName, "'box' not defined")
}
if box.Min.X >= wi || box.Min.Y >= hi || box.Max.X <= 0 || box.Max.Y <= 0 {
return nil, errors.Error(processName, "'box' outside rectangle")
}
out = &(*box)
if out.Min.X < 0 {
out.Max.X += out.Min.X
out.Min.X = 0
}
if out.Min.Y < 0 {
out.Max.Y += out.Min.Y
out.Min.Y = 0
}
// if w > wi
if out.Max.X > wi {
// max - min = wi - min
out.Max.X = wi
}
// if h > hi
if out.Max.Y > hi {
// max - min = hi - min
out.Max.Y = hi
}
return out, nil
}
// Rect create new rectangle box with the negative coordinates rules.
func Rect(x, y, w, h int) (*image.Rectangle, error) {
const processName = "bitmap.Rect"
if x < 0 {
w += x
x = 0
if w <= 0 {
return nil, errors.Errorf(processName, "x:'%d' < 0 and w: '%d' <= 0", x, w)
}
}
if y < 0 {
h += y
y = 0
if h <= 0 {
return nil, errors.Error(processName, "y < 0 and box off +quad")
}
}
box := image.Rect(x, y, x+w, y+h)
return &box, nil
}