mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 14:00:32 +08:00
git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@62634 954022d7-b5bf-4e40-9824-e11837661b57
442 lines
15 KiB
C++
442 lines
15 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
#include "jbig2arith.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#define restrict __restrict
|
|
#else
|
|
#define restrict __restrict__
|
|
#endif
|
|
|
|
#include <allheaders.h>
|
|
#include <pix.h>
|
|
|
|
#include <math.h>
|
|
|
|
#define S(i) symbols->pix[i]
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// iota isn't part of the STL standard, and it can be a pain to include even on
|
|
// gcc based systems. Thus we define it here and save the issues
|
|
// -----------------------------------------------------------------------------
|
|
template <class _ForwardIterator, class _Tp>
|
|
void
|
|
myiota(_ForwardIterator __first, _ForwardIterator __last, _Tp __val) {
|
|
while (__first != __last) *__first++ = __val++;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Sorts a vector of indexes into the symbols PIXA by height. This is needed
|
|
// because symbols are placed into the JBIG2 table in height order
|
|
// -----------------------------------------------------------------------------
|
|
class HeightSorter { // concept: stl/StrictWeakOrdering
|
|
public:
|
|
HeightSorter(const PIXA *isymbols)
|
|
: symbols(isymbols) {}
|
|
|
|
bool operator() (int x, int y) {
|
|
return S(x)->h < S(y)->h;
|
|
}
|
|
|
|
private:
|
|
const PIXA *const symbols;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Sorts a vector of indexes into the symbols PIXA by width. This is needed
|
|
// because symbols are placed into the JBIG2 table in width order (for a given
|
|
// height class)
|
|
// -----------------------------------------------------------------------------
|
|
class WidthSorter { // concept: stl/StrictWeakOrdering
|
|
public:
|
|
WidthSorter(const PIXA *isymbols)
|
|
: symbols(isymbols) {}
|
|
|
|
bool operator() (int x, int y) {
|
|
return S(x)->w < S(y)->w;
|
|
}
|
|
|
|
private:
|
|
const PIXA *const symbols;
|
|
};
|
|
|
|
static const int kBorderSize = 6;
|
|
|
|
// see comment in .h file
|
|
void
|
|
jbig2enc_symboltable(struct jbig2enc_ctx *restrict ctx,
|
|
PIXA *restrict const symbols,
|
|
std::vector<unsigned> *__restrict__ symbol_list,
|
|
std::map<int, int> *symmap, const bool unborder_symbols) {
|
|
const unsigned n = symbol_list->size();
|
|
int number = 0;
|
|
|
|
#ifdef JBIG2_DEBUGGING
|
|
fprintf(stderr, " symbols: %d\n", n);
|
|
#endif
|
|
|
|
// this is a vector of indexes into symbols
|
|
std::vector<unsigned> syms(*symbol_list);
|
|
// now sort that vector by height
|
|
std::sort(syms.begin(), syms.end(), HeightSorter(symbols));
|
|
|
|
// this is used for each height class to sort into increasing width
|
|
WidthSorter sorter(symbols);
|
|
|
|
// this stores the indexes of the symbols for a given height class
|
|
std::vector<int> hc;
|
|
// this keeps the value of the height of the current class
|
|
unsigned hcheight = 0;
|
|
for (unsigned i = 0; i < n;) {
|
|
// height is the height of this class of symbols
|
|
const unsigned height = S(syms[i])->h - (unborder_symbols ? 2*kBorderSize : 0);
|
|
#ifdef JBIG2_DEBUGGING
|
|
fprintf(stderr, "height is %d\n", height);
|
|
#endif
|
|
unsigned j;
|
|
hc.clear();
|
|
hc.push_back(syms[i]); // this is the first member of the new class
|
|
// walk the vector until we find a symbol with a different height
|
|
for (j = i + 1; j < n; ++j) {
|
|
if (S(syms[j])->h - (unborder_symbols ? 2*kBorderSize : 0) != height) break;
|
|
hc.push_back(syms[j]); // add each symbol of the same height to the class
|
|
}
|
|
#ifdef JBIG2_DEBUGGING
|
|
fprintf(stderr, " hc (height: %d, members: %d)\n", height, hc.size());
|
|
#endif
|
|
// all the symbols from i to j-1 are a height class
|
|
// now sort them into increasing width
|
|
sort(hc.begin(), hc.end(), sorter);
|
|
// encode the delta height
|
|
const int deltaheight = height - hcheight;
|
|
jbig2enc_int(ctx, JBIG2_IADH, deltaheight);
|
|
hcheight = height;
|
|
int symwidth = 0;
|
|
// encode each symbol
|
|
for (std::vector<int>::const_iterator k = hc.begin(); k != hc.end(); ++k) {
|
|
const int sym = *k;
|
|
const int thissymwidth = S(sym)->w - (unborder_symbols ? 2*kBorderSize : 0);
|
|
const int deltawidth = thissymwidth - symwidth;
|
|
#ifdef JBIG2_DEBUGGING
|
|
fprintf(stderr, " h: %d\n", S(sym)->w);
|
|
#endif
|
|
symwidth += deltawidth;
|
|
//fprintf(stderr, "width is %d\n", S(sym)->w);
|
|
jbig2enc_int(ctx, JBIG2_IADW, deltawidth);
|
|
|
|
PIX *unbordered;
|
|
if (unborder_symbols) {
|
|
// the exemplars are stored with a border
|
|
unbordered = pixRemoveBorder(S(sym), kBorderSize);
|
|
// encoding the bitmap requires that the pad bits be zero
|
|
} else {
|
|
unbordered = pixClone(S(sym));
|
|
}
|
|
pixSetPadBits(unbordered, 0);
|
|
jbig2enc_bitimage(ctx, (uint8_t *) unbordered->data, thissymwidth, height,
|
|
false);
|
|
// add this symbol to the map
|
|
(*symmap)[sym] = number++;
|
|
pixDestroy(&unbordered);
|
|
}
|
|
// OOB marks the end of the height class
|
|
//fprintf(stderr, "OOB\n");
|
|
jbig2enc_oob(ctx, JBIG2_IADW);
|
|
i = j;
|
|
}
|
|
|
|
// now we have the list of exported symbols (which is all of them)
|
|
// it's run length encoded and we have a run length of 0 (for all the symbols
|
|
// which aren't set) followed by a run length of the number of symbols
|
|
|
|
jbig2enc_int(ctx, JBIG2_IAEX, 0);
|
|
jbig2enc_int(ctx, JBIG2_IAEX, n);
|
|
|
|
jbig2enc_final(ctx);
|
|
}
|
|
|
|
// sort by the bottom-left corner of the box
|
|
class YSorter { // concept: stl/StrictWeakOrdering
|
|
public:
|
|
YSorter(const PTA *ill)
|
|
: ll(ill) {}
|
|
|
|
bool operator() (int x, int y) {
|
|
return ll->y[x] < ll->y[y];
|
|
}
|
|
|
|
private:
|
|
const PTA *const ll;
|
|
};
|
|
|
|
// sort by the bottom-left corner of the box
|
|
class XSorter { // concept: stl/StrictWeakOrdering
|
|
public:
|
|
XSorter(const PTA *ill)
|
|
: ll(ill) {}
|
|
|
|
bool operator() (int x, int y) {
|
|
return ll->x[x] < ll->x[y];
|
|
}
|
|
|
|
private:
|
|
const PTA *const ll;
|
|
};
|
|
|
|
#if (__GNUC__ <= 2) || defined(sun)
|
|
#define lrint(x) static_cast<int>(x)
|
|
#endif
|
|
|
|
#define BY(x) (lrint(ll->y[x]))
|
|
|
|
// see comment in .h file
|
|
void
|
|
jbig2enc_textregion(struct jbig2enc_ctx *restrict ctx,
|
|
/*const*/ std::map<int, int> &symmap,
|
|
/*const*/ std::map<int, int> &symmap2,
|
|
const std::vector<int> &comps,
|
|
PTA *const in_ll,
|
|
PIXA *const symbols,
|
|
NUMA *assignments, int stripwidth, int symbits,
|
|
PIXA *const source, BOXA *boxes, int baseindex,
|
|
int refine_level, bool unborder_symbols) {
|
|
// these are the only valid values for stripwidth
|
|
if (stripwidth != 1 && stripwidth != 2 && stripwidth != 4 &&
|
|
stripwidth != 8) {
|
|
abort();
|
|
}
|
|
|
|
PTA *ll;
|
|
|
|
// In the case of refinement, we have to put the symbols where the original
|
|
// boxes were. So we make up an array of lower-left (ll) points from the
|
|
// boxes. Otherwise we take the points from the in_ll array we were given.
|
|
// However, the in_ll array is absolutely indexed and the boxes array is
|
|
// relative to this page so watch out below.
|
|
if (source) {
|
|
ll = ptaCreate(0);
|
|
for (int i = 0; i < boxes->n; ++i) {
|
|
ptaAddPt(ll, boxes->box[i]->x,
|
|
boxes->box[i]->y + boxes->box[i]->h - 1);
|
|
}
|
|
} else {
|
|
// if we aren't doing refinement - we just put the symbols where they
|
|
// matched best
|
|
ll = in_ll;
|
|
}
|
|
|
|
const int n = comps.size();
|
|
|
|
// sort each box by distance from the top of the page
|
|
// syms (a copy of comps) is a list of indexes into symmap and ll
|
|
// elements which are indexes into symmap and ll are labeled I
|
|
// indexes into the syms array are labeled II
|
|
std::vector<int> syms(n);
|
|
if (source) {
|
|
// refining: fill syms with the numbers 0..n because ll is relative to this
|
|
// page in this case
|
|
myiota(syms.begin(), syms.end(), 0);
|
|
} else {
|
|
// fill syms with the component numbers from the comps array because ll is
|
|
// absolutly indexed in this case (absolute: over the whole multi-page
|
|
// document)
|
|
syms = comps;
|
|
}
|
|
// sort into height order
|
|
sort(syms.begin(), syms.end(), YSorter(ll));
|
|
|
|
XSorter sorter(ll);
|
|
|
|
int stript = 0;
|
|
int firsts = 0;
|
|
int wibble = 0;
|
|
// this is the initial stript value. I don't see why encoding this as zero,
|
|
// then encoding the first stript value as the real start is any worst than
|
|
// encoding this value correctly and then having a 0 value for the first
|
|
// deltat
|
|
jbig2enc_int(ctx, JBIG2_IADT, 0);
|
|
|
|
// for each symbol we group it into a strip, which is stripwidth px high
|
|
// for each strip we sort into left-right order
|
|
std::vector<int> strip; // elements of strip: I
|
|
for (int i = 0; i < n;) { // i: II
|
|
const int height = (BY(syms[i]) / stripwidth) * stripwidth;
|
|
int j;
|
|
strip.clear();
|
|
strip.push_back(syms[i]);
|
|
|
|
// now walk until we hit the first symbol which isn't in this strip
|
|
for (j = i + 1; j < n; ++j) { // j: II
|
|
if (BY(syms[j]) < height) abort();
|
|
if (BY(syms[j]) >= height + stripwidth) {
|
|
// outside strip
|
|
break;
|
|
}
|
|
strip.push_back(syms[j]);
|
|
}
|
|
|
|
// now sort the strip into left-right order
|
|
sort(strip.begin(), strip.end(), sorter);
|
|
const int deltat = height - stript;
|
|
#ifdef SYM_DEBUGGING
|
|
fprintf(stderr, "deltat is %d\n", deltat);
|
|
#endif
|
|
jbig2enc_int(ctx, JBIG2_IADT, deltat / stripwidth);
|
|
stript = height;
|
|
#ifdef SYM_DEBUGGING
|
|
fprintf(stderr, "t now: %d\n", stript);
|
|
#endif
|
|
|
|
bool firstsymbol = true;
|
|
int curs = 0;
|
|
// k: iterator(I)
|
|
for (std::vector<int>::const_iterator k = strip.begin(); k != strip.end(); ++k) {
|
|
const int sym = *k; // sym: I
|
|
if (firstsymbol) {
|
|
firstsymbol = false;
|
|
const int deltafs = lrint(ll->x[sym]) - firsts;
|
|
jbig2enc_int(ctx, JBIG2_IAFS, deltafs);
|
|
firsts += deltafs;
|
|
curs = firsts;
|
|
} else {
|
|
const int deltas = lrint(ll->x[sym]) - curs;
|
|
jbig2enc_int(ctx, JBIG2_IADS, deltas);
|
|
curs += deltas;
|
|
}
|
|
|
|
// if stripwidth is 1, all the t values must be the same so they aren't
|
|
// even encoded
|
|
if (stripwidth > 1) {
|
|
const int deltat = BY(sym) - stript;
|
|
jbig2enc_int(ctx, JBIG2_IAIT, deltat);
|
|
}
|
|
|
|
// The assignments array is absolutely indexed, but in the case that we
|
|
// are doing refinement (source != NULL) then the symbol number is
|
|
// relative to this page, so we have to add the baseindex to get an
|
|
// absolute index.
|
|
const int assigned = (int)assignments->array
|
|
[sym + (source ? baseindex : 0)];
|
|
|
|
// the symmap maps the number of the symbol from the classifier to the
|
|
// order in while it was written in the symbol dict
|
|
|
|
// We have two symbol dictionaries. A global one and a per-page one.
|
|
int symid;
|
|
std::map<int, int>::const_iterator symit = symmap.find(assigned);
|
|
if (symit != symmap.end()) {
|
|
symid = symit->second;
|
|
} else {
|
|
symit = symmap2.find(assigned);
|
|
if (symit != symmap2.end()) {
|
|
symid = symit->second + symmap.size();
|
|
} else {
|
|
for (symit = symmap.begin(); symit != symmap.end(); ++symit) {
|
|
fprintf(stderr, "%d ", symit->first);
|
|
}
|
|
for (symit = symmap2.begin(); symit != symmap2.end(); ++symit) {
|
|
fprintf(stderr, "%d ", symit->first);
|
|
}
|
|
fprintf(stderr, "\n%d\n", assigned);
|
|
abort();
|
|
}
|
|
}
|
|
#ifdef SYM_DEBUGGING
|
|
fprintf(stderr, "sym: %d\n", symid);
|
|
#endif
|
|
jbig2enc_iaid(ctx, symbits, symid);
|
|
|
|
// refinement is enabled if the original source components are given
|
|
if (source) {
|
|
// the boxes array is indexed by the number of the symbol on this page.
|
|
// So we subtract the number of the first symbol to get this relative
|
|
// number.
|
|
const int abssym = baseindex + sym;
|
|
|
|
PIX *symbol;
|
|
if (unborder_symbols) {
|
|
// the symbol has a 6 px border around it, which we need to remove
|
|
symbol = pixRemoveBorder(S(assigned), kBorderSize);
|
|
} else {
|
|
symbol = pixClone(S(assigned));
|
|
}
|
|
pixSetPadBits(symbol, 0);
|
|
|
|
const int targetw = boxes->box[sym]->w;
|
|
const int targeth = boxes->box[sym]->h;
|
|
const int targetx = boxes->box[sym]->x;
|
|
const int targety = boxes->box[sym]->y;
|
|
|
|
const int symboly = (int) (in_ll->y[abssym] - symbol->h) + 1;
|
|
const int symbolx = (int) in_ll->x[abssym];
|
|
|
|
const int deltaw = targetw - symbol->w;
|
|
const int deltah = targeth - symbol->h;
|
|
const int deltax = targetx - symbolx;
|
|
const int deltay = targety - symboly;
|
|
|
|
pixSetPadBits(source->pix[sym], 0);
|
|
// now see how well the symbol matches
|
|
PIX *targetcopy = pixCopy(NULL, source->pix[sym]);
|
|
pixRasterop(targetcopy, deltax, deltay, symbol->w, symbol->h,
|
|
PIX_SRC ^ PIX_DST,
|
|
symbol, 0, 0);
|
|
int deltacount;
|
|
pixCountPixels(targetcopy, &deltacount, NULL);
|
|
#ifdef SYMBOL_COMPRESSION_DEBUGGING
|
|
fprintf(stderr, "delta count: %d\n", deltacount);
|
|
#endif
|
|
pixDestroy(&targetcopy);
|
|
|
|
#ifdef SYMBOL_COMPRESSION_DEBUGGING
|
|
fprintf(stderr, "refinement: dw:%d dh:%d dx:%d dy:%d w:%d h:%d\n",
|
|
deltaw, deltah, deltax, deltay, targetw, targeth);
|
|
fprintf(stderr, " box: %d %d symbol: %d %d h:%d ll:%f %f\n",
|
|
targetx, targety, symbolx, symboly, symbol->h,
|
|
in_ll->x[abssym], in_ll->y[abssym]);
|
|
#endif
|
|
|
|
// Note that the refinement encoding function can only cope with x
|
|
// offsets in [-1, 0, 1] so refinement is disabled if the offset is
|
|
// outside this range. This should be *very* rare.
|
|
if (deltacount <= refine_level || deltax < -1 || deltax > 1) {
|
|
//if (deltaw > 1 || deltaw < -1 || deltax || deltah || deltay) {
|
|
// refinement disabled.
|
|
jbig2enc_int(ctx, JBIG2_IARI, 0);
|
|
// update curs given the width of the bitmap
|
|
curs += (S(assigned)->w - (unborder_symbols ? 2*kBorderSize : 0)) - 1;
|
|
} else {
|
|
wibble++;
|
|
jbig2enc_int(ctx, JBIG2_IARI, 1);
|
|
|
|
jbig2enc_int(ctx, JBIG2_IARDW, deltaw);
|
|
jbig2enc_int(ctx, JBIG2_IARDH, deltah);
|
|
jbig2enc_int(ctx, JBIG2_IARDX, deltax - (deltaw >> 1));
|
|
jbig2enc_int(ctx, JBIG2_IARDY, deltay - (deltah >> 1));
|
|
|
|
jbig2enc_refine
|
|
(ctx, (uint8_t *) symbol->data, symbol->w, symbol->h,
|
|
(uint8_t *) source->pix[sym]->data, targetw, targeth,
|
|
deltax, -deltay);
|
|
|
|
pixDestroy(&symbol);
|
|
curs += targetw - 1;
|
|
}
|
|
} else {
|
|
// update curs given the width of the bitmap
|
|
curs += (S(assigned)->w - (unborder_symbols ? 2*kBorderSize : 0)) - 1;
|
|
}
|
|
}
|
|
// terminate the strip
|
|
jbig2enc_oob(ctx, JBIG2_IADS);
|
|
i = j;
|
|
}
|
|
|
|
jbig2enc_final(ctx);
|
|
if (ll != in_ll) ptaDestroy(&ll);
|
|
}
|