213 lines
6.0 KiB
Go
Raw Normal View History

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 12:47:41 +01:00
/*
* 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
}