home *** CD-ROM | disk | FTP | other *** search
- //========================================================================
- //
- // AOutputDev.cc
- //
- // adapted from XOutputDev.cc - Copyright 1996 Derek B. Noonburg
- //
- // changes are copyright 1999 Emmanuel Lesueur
- //
- //========================================================================
-
- #ifdef __GNUC__
- #pragma implementation
- #endif
-
- #define DB(x) //x
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
- #include <ctype.h>
- #include <math.h>
- #define Object ZZObject
- #include <graphics/gfx.h>
- #include <graphics/text.h>
- #undef Object
- #include "gmem.h"
- #include "GString.h"
- #include "Object.h"
- #include "Stream.h"
- #include "GfxState.h"
- #include "GfxFont.h"
- #include "Error.h"
- #include "TextOutputDev.h"
- #include "AOutputDev.h"
- #include "AGfx.h"
- #include "FontOutputDev.h"
-
-
- //------------------------------------------------------------------------
- // Constants and macros
- //------------------------------------------------------------------------
-
- inline int xoutRound(double x) { return int(x + 0.5); }
-
- const int maxCurveSplits=6; // max number of splits when recursively
- // drawing Bezier curves
-
- PArea<short> xoutRound(const PArea<double>& a) {
- PArea<short> r;
- r.rule(PArea<short>::Rule(a.rule()));
- r.reserve(a.size());
- for(PArea<double>::const_iterator p(a.begin()); p != a.end(); ++p) {
- Polygon<short> t;
- t.reserve(p->size());
- for(Polygon<double>::const_iterator v(p->begin()); v != p->end(); ++v)
- t.push_back(Vertex<short>(xoutRound(v->x), xoutRound(v->y)));
- r.push_back(t);
- }
- return r;
- }
-
- //------------------------------------------------------------------------
- // Parameters
- //------------------------------------------------------------------------
-
- extern int maxColors;
-
- //------------------------------------------------------------------------
- // ColorTable
- //------------------------------------------------------------------------
-
- Gulong ColorTable::add(Gulong x) {
- if (num == maxEntries) {
- maxEntries = (maxEntries * 3) / 2 + 16;
- entries = (entry*) grealloc(entries, maxEntries*sizeof(entry));
- }
- Gulong n = num;
- entry *p = entries;
- while (n > 0) {
- Gulong k = n / 2;
- entry *q = p + k;
- if (x > q->val) {
- p = q + 1;
- n -= k + 1;
- } else if(x != q->val)
- n = k;
- else {
- p = q;
- break;
- }
- }
- if (p != entries + num)
- memmove(p+1, p, (entries+num-p)*sizeof(entry));
- p->n = num;
- p->val = x;
- return num++;
- }
-
- Gulong ColorTable::find(Gulong x) const {
- Gulong n = num;
- entry *p = entries;
- while (n > 0) {
- Gulong k = n / 2;
- entry *q = p + k;
- if (x > q->val) {
- p = q + 1;
- n -= k + 1;
- } else if(x != q->val)
- n = k;
- else
- return q->n;
- }
- return Gulong(-1);
- }
-
- // find the RGB value of pen n
- // slow, but only used int drawImageMask...
- Gulong ColorTable::find_rgb(Gulong n) const {
- if (n < num) {
- entry *p = entries;
- while (p->n != n)
- ++p;
- return p->val;
- }
- return 0; // should not happen...
- }
-
- //------------------------------------------------------------------------
- // Constructed characters
- //------------------------------------------------------------------------
-
- #define lastRegularChar 0x0ff
- #define firstSubstChar 0x100
- #define lastSubstChar 0x104
- #define firstConstrChar 0x105
- #define lastConstrChar 0x106
- #define firstMultiChar 0x107
- #define lastMultiChar 0x10d
-
- // substituted chars
- static Guchar substChars[] = {
- 0x27, // 100: quotesingle --> quoteright
- 0x2d, // 101: emdash --> hyphen
- 0xad, // 102: hyphen --> endash
- 0x2f, // 103: fraction --> slash
- 0xb0, // 104: ring --> degree
- };
-
- // constructed chars
- // 105: bullet
- // 106: trademark
-
- // built-up chars
- static char *multiChars[] = {
- "fi", // 107: fi
- "fl", // 108: fl
- "OE", // 109: OE
- "oe", // 10a: oe
- "...", // 10b: ellipsis
- "``", // 10c: quotedblleft
- "''" // 10d: quotedblright
- };
-
- // ignored chars
- // 10c: Lslash
- // 10d: Scaron
- // 10e: Zcaron
- // 10f: Ydieresis
- // 110: breve
- // 111: caron
- // 112: circumflex
- // 113: dagger
- // 114: daggerdbl
- // 115: dotaccent
- // 116: dotlessi
- // 117: florin
- // 118: grave
- // 119: guilsinglleft
- // 11a: guilsinglright
- // 11b: hungarumlaut
- // 11c: lslash
- // 11d: ogonek
- // 11e: perthousand
- // 11f: quotedblbase
- // 120: quotesinglbase
- // 121: scaron
- // 122: tilde
- // 123: zcaron
-
- //------------------------------------------------------------------------
- // AOutputFont
- //------------------------------------------------------------------------
-
- int AOutputFont::xFontCount;
-
- // Note: if real font is substantially narrower than substituted
- // font, the size is reduced accordingly.
- AOutputFont::AOutputFont(AGfx& gfx1, GfxFont *gfxFont, double m11, double m12,
- double m21, double m22) : gfx(gfx1) {
- GfxFontEncoding *encoding;
- const char *fontName;
- int fontStyle;
- //GBool rotated;
- double size;
- int startSize, sz;
- int index;
- int code, code2;
- char *charName;
- int n;
- double w1, w2;
-
- // init
- id = gfxFont->getID();
- mat11 = m11;
- mat12 = m12;
- mat21 = m21;
- mat22 = m22;
- xFont = NULL;
- hex = gFalse;
-
- // construct X font name
- fontName = getAFont(gfxFont, encoding, fontStyle, w1, w2);
- m11 *= w1;
- m12 *= w2;
- m21 *= w1;
- m22 *= w2;
-
- // Construct forward and reverse map.
- // This tries to deal with font subset character names of the
- // form 'Bxx', 'Cxx', 'Gxx', with decimal or hex numbering.
-
- for (code = 0; code < 256; ++code)
- revMap[code] = 0;
- if (encoding) {
- for (code = 0; code < 256; ++code) {
- if ((charName = gfxFont->getCharName(code))) {
- if ((charName[0] == 'B' || charName[0] == 'C' ||
- charName[0] == 'G') &&
- strlen(charName) == 3 &&
- ((charName[1] >= 'a' && charName[1] <= 'f') ||
- (charName[1] >= 'A' && charName[1] <= 'F') ||
- (charName[2] >= 'a' && charName[2] <= 'f') ||
- (charName[2] >= 'A' && charName[2] <= 'F'))) {
- hex = gTrue;
- break;
- }
- }
- }
- for (code = 0; code < 256; ++code) {
- if ((charName = gfxFont->getCharName(code))) {
- if ((code2 = encoding->getCharCode(charName)) < 0) {
- n = strlen(charName);
- if (hex && n == 3 &&
- (charName[0] == 'B' || charName[0] == 'C' ||
- charName[0] == 'G') &&
- isxdigit(charName[1]) && isxdigit(charName[2])) {
- //sscanf(charName+1, "%x", &code2);
- code2 = strtol(charName+1, NULL, 16);
- } else if (!hex && n >= 2 && n <= 3 &&
- isdigit(charName[0]) && isdigit(charName[1])) {
- code2 = atoi(charName);
- if (code2 >= 256)
- code2 = -1;
- } else if (!hex && n >= 3 && n <= 5 && isdigit(charName[1])) {
- code2 = atoi(charName+1);
- if (code2 >= 256)
- code2 = -1;
- }
- //~ this is a kludge -- is there a standard internal encoding
- //~ used by all/most Type 1 fonts?
- if (code2 == 262) // hyphen
- code2 = 45;
- else if (code2 == 266) // emdash
- code2 = 208;
- }
- if (code2 >= 0) {
- map[code] = (Gushort)code2;
- if (code2 < 256)
- revMap[code2] = (Guchar)code;
- } else {
- map[code] = 0;
- }
- } else {
- map[code] = 0;
- }
- }
- } else {
- code2 = 0; // to make gcc happy
- //~ this is a hack to get around the fact that X won't draw
- //~ chars 0..31; this works when the fonts have duplicate encodings
- //~ for those chars
- for (code = 0; code < 32; ++code) {
- if ((charName = gfxFont->getCharName(code)) &&
- (code2 = gfxFont->getCharCode(charName)) >= 0) {
- map[code] = (Gushort)code2;
- if (code2 < 256)
- revMap[code2] = (Guchar)code;
- }
- }
- for (code = 32; code < 256; ++code) {
- map[code] = (Gushort)code;
- revMap[code] = (Guchar)code;
- }
- }
-
- // compute size, normalize matrix
- size = sqrt(m21*m21 + m22*m22);
- m11 = m11 / size;
- m12 = -m12 / size;
- m21 = m21 / size;
- m22 = -m22 / size;
- startSize = (int)size;
-
- // try to get a rotated font?
- /*rotated = !(mat11 > 0 && mat22 > 0 && fabs(mat11/mat22 - 1) < 0.2 &&
- fabs(mat12) < 0.01 && fabs(mat21) < 0.01);
-
- // open X font -- if font is not found (which means the server can't
- // scale fonts), try progressively smaller and then larger sizes
- //~ This does a linear search -- it should get a list of fonts from
- //~ the server and pick the closest.
- if (rotated)
- sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
- m11<0 ? "~" : "", fabs(m11 * startSize),
- m12<0 ? "~" : "", fabs(m12 * startSize),
- m21<0 ? "~" : "", fabs(m21 * startSize),
- m22<0 ? "~" : "", fabs(m22 * startSize));
- else
- sprintf(fontSize, "%d", startSize);*/
-
- xFont = xFontCount++;
- gfx.openfont(xFont, fontName, startSize, fontStyle);
- }
-
- AOutputFont::~AOutputFont() {
- gfx.closefont(xFont);
- }
-
- //------------------------------------------------------------------------
- // AOutputFontCache
- //------------------------------------------------------------------------
-
- AOutputFontCache::AOutputFontCache(AGfx& g) : gfx(g) {
- int i;
-
- for (i = 0; i < fontCacheSize; ++i)
- fonts[i] = NULL;
- numFonts = 0;
- }
-
- AOutputFontCache::~AOutputFontCache() {
- int i;
-
- for (i = 0; i < numFonts; ++i)
- delete fonts[i];
- }
-
- AOutputFont *AOutputFontCache::getFont(GfxFont *gfxFont,
- double m11, double m12,
- double m21, double m22) {
- AOutputFont *font;
- int i, j;
-
- // is it the most recently used font?
- if (numFonts > 0 && fonts[0]->matches(gfxFont->getID(),
- m11, m12, m21, m22))
- return fonts[0];
-
- // is it in the cache?
- for (i = 1; i < numFonts; ++i) {
- if (fonts[i]->matches(gfxFont->getID(), m11, m12, m21, m22)) {
- font = fonts[i];
- for (j = i; j > 0; --j)
- fonts[j] = fonts[j-1];
- fonts[0] = font;
- return font;
- }
- }
-
- // make a new font
- font = new AOutputFont(gfx, gfxFont, m11, m12, m21, m22);
- /*if (!font->getTextFont()) {
- delete font;
- return NULL;
- }*/
-
- // insert font in cache
- if (numFonts == fontCacheSize) {
- --numFonts;
- delete fonts[numFonts];
- }
- for (j = numFonts; j > 0; --j)
- fonts[j] = fonts[j-1];
- fonts[0] = font;
- ++numFonts;
-
- // return it
- return font;
- }
-
- //------------------------------------------------------------------------
- // AOutputDev
- //------------------------------------------------------------------------
-
- AOutputDev::AOutputDev(AGfx& gfx1, Guint depth1, ColorMap* colormap1)
- : gfx(gfx1) {
- Gulong mask;
- int r, g, b, n, m, i;
- GBool ok;
-
- // get display/pixmap info
- depth = depth1;
- colormap = colormap1;
-
- rShift = 4;
- gShift = 4;
- bShift = 4;
- rMul = (1 << rShift) - 1;
- gMul = (1 << gShift) - 1;
- bMul = (1 << bShift) - 1;
- colorMask = 0x00f0f0f0;
-
- // check for TrueColor visual
- trueColor = depth>8;
-
- // set up the font cache and fonts
- gfxFont = NULL;
- font = NULL;
- fontCache = new AOutputFontCache(gfx);
-
- // create text object
- text = new TextPage(gFalse);
- }
-
- AOutputDev::~AOutputDev() {
- delete fontCache;
- delete text;
- }
-
- void AOutputDev::startPage(int pageNum, GfxState *state) {
- GfxColor color;
-
- capStyle = CapButt;
- joinStyle = JoinBevel;
- curColor = penColor;
- strokeColor = curColor;
- fillColor = curColor;
- linkColor = Gulong(-1);
-
- clipRegion.clear();
- installClip();
- Polygon<double> p;
- p.reserve(4);
- p.push_back(Vertex<double>(0,0));
- p.push_back(Vertex<double>(10000,0));
- p.push_back(Vertex<double>(10000,10000));
- p.push_back(Vertex<double>(0,10000));
- clipRegion.push_back(p);
- clipStack.clear();
-
- // default line flatness
- flatness = 0;
-
- // clear font
- gfxFont = NULL;
- font = NULL;
-
- // clear text object
- text->clear();
-
- gfx.init();
- gfx.start_page();
- colorTable.clear();
- colorTable.add(0x000000UL);
- colorTable.add(0xf0f0f0UL);
- }
-
- void AOutputDev::endPage() {
- gfx.end_page();
- gfx.flush();
- text->coalesce();
- }
-
- void AOutputDev::drawLinkBorder(double x1, double y1, double x2, double y2,
- double w) {
- int x, y;
- DB(printf("\ndrawlink(%f,%f,%f,%f,%f)\n",x1,y1,x2,y2,w);)
- if (linkColor == Gulong(-1)) {
- GfxColor color;
- color.setRGB(0, 0, 1);
- linkColor = findColor(&color);
- }
- if (curColor != linkColor) {
- curColor = linkColor;
- gfx.setapen(curColor);
- }
- //XSetLineAttributes(display, strokeGC, xoutRound(w),
- // LineSolid, CapRound, JoinRound);
- Polygon<short> points;
- points.reserve(5);
- cvtUserToDev(x1, y1, &x, &y);
- Vertex<short> orig(x,y);
- points.push_back(orig);
- cvtUserToDev(x2, y1, &x, &y);
- points.push_back(Vertex<short>(x,y));
- cvtUserToDev(x2, y2, &x, &y);
- points.push_back(Vertex<short>(x,y));
- cvtUserToDev(x1, y2, &x, &y);
- points.push_back(Vertex<short>(x,y));
- points.push_back(orig);
- gfx.polydraw(points);
- }
-
- void AOutputDev::saveState(GfxState *state) {
- DB(printf("\nsave state\n");)
- clipStack.push_back(clipRegion);
- }
-
- void AOutputDev::restoreState(GfxState *state) {
- DB(printf("\nrestore state\n");)
- clipRegion = clipStack.back();
- clipStack.pop_back();
- curColor = Gulong(-1);
- updateAll(state);
- installClip();
- }
-
- void AOutputDev::updateAll(GfxState *state) {
- updateLineAttrs(state, gTrue);
- updateFlatness(state);
- updateMiterLimit(state);
- updateFillColor(state);
- updateStrokeColor(state);
- updateFont(state);
- }
-
- void AOutputDev::updateCTM(GfxState *state, double m11, double m12,
- double m21, double m22, double m31, double m32) {
- updateLineAttrs(state, gTrue);
- }
-
- void AOutputDev::updateLineDash(GfxState *state) {
- updateLineAttrs(state, gTrue);
- }
-
- void AOutputDev::updateFlatness(GfxState *state) {
- flatness = state->getFlatness();
- }
-
- void AOutputDev::updateLineJoin(GfxState *state) {
- updateLineAttrs(state, gFalse);
- }
-
- void AOutputDev::updateLineCap(GfxState *state) {
- updateLineAttrs(state, gFalse);
- }
-
- // unimplemented
- void AOutputDev::updateMiterLimit(GfxState *state) {
- }
-
- void AOutputDev::updateLineWidth(GfxState *state) {
- updateLineAttrs(state, gFalse);
- }
-
- void AOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
- CapStyle cap;
- JoinStyle join;
- double *dashPattern;
- int dashLength;
- double dashStart;
- int i;
-
- lineWidth = state->getTransformedLineWidth();
- switch (state->getLineCap()) {
- case 0: cap = CapButt; break;
- case 1: cap = CapRound; break;
- case 2: cap = CapProjecting; break;
- default:
- error(-1, "Bad line cap style (%d)", state->getLineCap());
- cap = CapButt;
- break;
- }
- capStyle = cap;
- switch (state->getLineJoin()) {
- case 0: join = JoinMiter; break;
- case 1: join = JoinRound; break;
- case 2: join = JoinBevel; break;
- default:
- error(-1, "Bad line join style (%d)", state->getLineJoin());
- join = JoinMiter;
- break;
- }
- joinStyle = join;
- state->getLineDash(&dashPattern, &dashLength, &dashStart);
- /*XSetLineAttributes(display, strokeGC, xoutRound(width),
- dashLength > 0 ? LineOnOffDash : LineSolid,
- cap, join);*/
- if (updateDash) {
- unsigned mask = 0xffff;
- short start = 0;
- if (dashLength > 0) {
- int n = 16;
- for (i = 0; i < dashLength; ++i) {
- int k = xoutRound(state->transformWidth(dashPattern[i]));
- if (k < n) {
- mask ^= (1 << n-k) - 1;
- n -= k;
- } else
- break;
- }
- start = xoutRound(dashStart);
- }
- gfx.set_line_ptrn(mask, start);
- }
- }
-
- void AOutputDev::updateFillColor(GfxState *state) {
- fillColor = findColor(state->getFillColor());
- DB(printf("\nfill color=%d\n",fillColor);)
- }
-
- void AOutputDev::updateStrokeColor(GfxState *state) {
- strokeColor = findColor(state->getStrokeColor());
- DB(printf("\nstroke color=%d\n",strokeColor);)
- }
-
- void AOutputDev::updateFont(GfxState *state) {
- double m11, m12, m21, m22;
-
- if (!(gfxFont = state->getFont())) {
- font = NULL;
- return;
- }
- state->getFontTransMat(&m11, &m12, &m21, &m22);
- m11 *= state->getHorizScaling();
- m21 *= state->getHorizScaling();
- font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
- if (font)
- gfx.setfont(font->getTextFont());
- else {
- DB(printf("\nupdateFont failed.\n");)
- }
- }
-
- void AOutputDev::stroke(GfxState *state) {
-
- if (curColor != strokeColor) {
- curColor = strokeColor;
- gfx.setapen(curColor);
- }
-
- // transform points
- PArea<double> area(convertPath(state));
-
- // draw each subpath
- if (lineWidth <= 1.5) {
-
- PArea<short> iarea(xoutRound(area));
- for (PArea<short>::const_iterator p(iarea.begin()); p != iarea.end(); ++p)
- if(!p->empty()) {
- DB(printf("\nstroke(%d)\n",p->size());)
- gfx.polydraw(*p);
- }
-
- } else {
-
- for (PArea<double>::const_iterator q(area.begin()); q != area.end(); ++q) {
- Polygon<double>::const_iterator p(q->begin());
- for (int k = 1; k < q->size(); ++k, ++p) {
- if (floor(p[0].x + 0.5) == floor(p[1].x + 0.5)) {
- DB(printf("\nthickVstroke\n");)
- short x1 = xoutRound(p[0].x - lineWidth/2);
- short x2 = xoutRound(p[0].x + lineWidth/2);
- short y1 = xoutRound(p[0].y);
- short y2 = xoutRound(p[1].y);
- if (y1 > y2) {
- short t = y1;
- y1 = y2;
- y2 = t;
- }
- gfx.rectfill(x1,y1,x2,y2);
- } else if (floor(p[0].y + 0.5) == floor(p[1].y + 0.5)) {
- DB(printf("\nthickHstroke\n");)
- short y1 = xoutRound(p[0].y - lineWidth/2);
- short y2 = xoutRound(p[0].y + lineWidth/2);
- short x1 = xoutRound(p[0].x);
- short x2 = xoutRound(p[1].x);
- if (x1 > x2) {
- short t = x1;
- x1 = x2;
- x2 = t;
- }
- gfx.rectfill(x1,y1,x2,y2);
- } else {
- DB(printf("\nthickstroke\n");)
- double w = lineWidth/2;
- double dx = p[1].x - p[0].x;
- double dy = p[1].y - p[0].y;
- double l = sqrt(dx*dx+dy*dy);
- double c = dx/l;
- double s = dy/l;
- Polygon<short> pts;
- pts.reserve(4);
- pts.push_back(Vertex<short>(xoutRound(p[0].x - w*s),
- xoutRound(p[0].y - w*c)));
- pts.push_back(Vertex<short>(xoutRound(p[1].x - w*s),
- xoutRound(p[1].y - w*c)));
- pts.push_back(Vertex<short>(xoutRound(p[1].x + w*s),
- xoutRound(p[1].y + w*c)));
- pts.push_back(Vertex<short>(xoutRound(p[0].x + w*s),
- xoutRound(p[0].y + w*c)));
- AGfx::AreaDrawer(gfx).add(pts);
- }
- }
- }
- }
- }
-
- void AOutputDev::fill(GfxState *state) {
- DB(printf("\nfill\n");)
- doFill(state, PArea<double>::winding);
- }
-
- void AOutputDev::eoFill(GfxState *state) {
- DB(printf("\neofill\n");)
- doFill(state, PArea<double>::even_odd);
- }
-
- void AOutputDev::doFill(GfxState *state, int rule) {
-
- if (curColor != fillColor) {
- curColor = fillColor;
- gfx.setapen(curColor);
- }
-
- // transform points
- PArea<short> area;
- {
- PArea<double> t(convertPath(state));
- t.rule(PArea<double>::Rule(rule));
- t.reduce();
- if(rule == PArea<double>::winding)
- t = t.make_convex_union();
- area = xoutRound(t);
- }
-
- /*for (PArea<short>::const_iterator p(area.begin()); p != area.end(); ++p)
- if(!p->empty()) {
- Polygon<short> q(*p);
- DB(printf("\nfill(%d)\n",q.size());)
- q.push_back(*q.begin());
- gfx.polydraw(q);
- }*/
-
- // fill them
- if(area.is_disjoint_rectangle_union()) {
- for(PArea<short>::const_iterator p(area.begin()); p != area.end(); ++p) {
- if(!p->empty()) {
- Polygon<short>::const_iterator points(p->begin());
- short x1,x2,y1,y2;
- if(points[0].x>points[2].x) {
- x1=points[2].x;
- x2=points[0].x;
- } else {
- x1=points[0].x;
- x2=points[2].x;
- }
- if(points[0].y>points[2].y) {
- y1=points[2].y;
- y2=points[0].y;
- } else {
- y1=points[0].y;
- y2=points[2].y;
- }
- DB(printf("rectfill: (%d,%d)-(%d,%d)\n",x1,y1,x2,y2);)
- gfx.rectfill(x1,y1,x2,y2);
- }
- }
- } else {
- DB(printf("polyfill:");)
- AGfx::AreaDrawer ad(gfx);
- for(PArea<short>::const_iterator p(area.begin()); p != area.end(); ++p)
- ad.add(*p);
- }
-
- }
-
- void AOutputDev::clip(GfxState *state) {
- DB(printf("\nclip\n");)
- doClip(state, PArea<double>::winding);
- }
-
- void AOutputDev::eoClip(GfxState *state) {
- DB(printf("\neoclip\n");)
- doClip(state, PArea<double>::even_odd);
- }
-
- void AOutputDev::doClip(GfxState *state, int rule) {
-
- // transform points
- PArea<double> area(convertPath(state));
- area.rule(PArea<double>::Rule(rule));
-
- // open closed paths
- area.reduce();
-
- area = area.make_convex_union();
-
- // intersect with clipping region
- clipRegion = clipRegion & area;
- clipRegion.reduce();
-
- installClip();
- }
-
- void AOutputDev::installClip() {
- PArea<short> c(xoutRound(clipRegion));
- gfx.clearclip();
- for(PArea<short>::const_iterator p(c.begin()); p != c.end(); ++p) {
- if(!p->empty()) {
- if(p->is_rectangle()) {
- Polygon<short>::const_iterator points(p->begin());
- short x1,x2,y1,y2;
- if(points[0].x>points[2].x) {
- x1=points[2].x;
- x2=points[0].x;
- } else {
- x1=points[0].x;
- x2=points[2].x;
- }
- if(points[0].y>points[2].y) {
- y1=points[2].y;
- y2=points[0].y;
- } else {
- y1=points[0].y;
- y2=points[2].y;
- }
- DB(printf("cliprect: (%d,%d)-(%d,%d)\n",x1,y1,x2,y2);)
- gfx.rectclip(x1,y1,x2,y2);
- } else {
- DB(printf("clippoly(%d):\n",p->size());)
- gfx.polyclip(*p);
- }
- }
- }
- gfx.installclip();
- }
-
- //
- // Transform points in the path and convert curves to line segments.
- //
- PArea<double> AOutputDev::convertPath(GfxState *state) {
- DB(printf("path[");)
- PArea<double> res;
-
- // get path and number of subpaths
- GfxPath *path = state->getPath();
- int n = path->getNumSubpaths();
-
- // do each subpath
- for (int i = 0; i < n; ++i)
- res.push_back(convertSubpath(state, path->getSubpath(i)));
-
- DB(printf("]\n");)
- return res;
- }
-
- //
- // Transform points in a single subpath and convert curves to line
- // segments.
- //
- Polygon<double> AOutputDev::convertSubpath(GfxState *state, GfxSubpath *subpath) {
- DB(printf("[");)
- DB(int k = 0;)
- double x0, y0, x1, y1, x2, y2, x3, y3;
- int m, i;
- Polygon<double> poly;
-
- m = subpath->getNumPoints();
- i = 0;
- while (i < m) {
- if (i >= 1 && subpath->getCurve(i)) {
- state->transform(subpath->getX(i-1), subpath->getY(i-1), &x0, &y0);
- state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
- state->transform(subpath->getX(i+1), subpath->getY(i+1), &x2, &y2);
- state->transform(subpath->getX(i+2), subpath->getY(i+2), &x3, &y3);
- doCurve(poly, x0, y0, x1, y1, x2, y2, x3, y3);
- i += 3;
- } else {
- state->transform(subpath->getX(i), subpath->getY(i), &x1, &y1);
- DB(if(!(k++&3))printf("\n");printf(" (%g,%g)",x1,y1);)
- poly.push_back(Vertex<double>(x1, y1));
- ++i;
- }
- }
- DB(printf(" ]\n");)
- return poly;
- }
-
- //
- // Subdivide a Bezier curve. This uses floating point to avoid
- // propagating rounding errors. (The curves look noticeably more
- // jagged with integer arithmetic.)
- //
- void AOutputDev::doCurve(Polygon<double>& poly,
- double x0, double y0, double x1, double y1,
- double x2, double y2, double x3, double y3) {
- double x[(1<<maxCurveSplits)+1][3];
- double y[(1<<maxCurveSplits)+1][3];
- int next[1<<maxCurveSplits];
- int p1, p2, p3;
- double xx1, yy1, xx2, yy2;
- double dx, dy, mx, my, d1, d2;
- double xl0, yl0, xl1, yl1, xl2, yl2;
- double xr0, yr0, xr1, yr1, xr2, yr2, xr3, yr3;
- double xh, yh;
- double flat;
-
- flat = (double)(flatness * flatness);
- if (flat < 1)
- flat = 1;
-
- // initial segment
- p1 = 0;
- p2 = 1<<maxCurveSplits;
- x[p1][0] = x0; y[p1][0] = y0;
- x[p1][1] = x1; y[p1][1] = y1;
- x[p1][2] = x2; y[p1][2] = y2;
- x[p2][0] = x3; y[p2][0] = y3;
- next[p1] = p2;
-
- while (p1 < (1<<maxCurveSplits)) {
-
- // get next segment
- xl0 = x[p1][0]; yl0 = y[p1][0];
- xx1 = x[p1][1]; yy1 = y[p1][1];
- xx2 = x[p1][2]; yy2 = y[p1][2];
- p2 = next[p1];
- xr3 = x[p2][0]; yr3 = y[p2][0];
-
- // compute distances from control points to midpoint of the
- // straight line (this is a bit of a hack, but it's much faster
- // than computing the actual distances to the line)
- mx = (xl0 + xr3) * 0.5;
- my = (yl0 + yr3) * 0.5;
- dx = xx1 - mx;
- dy = yy1 - my;
- d1 = dx*dx + dy*dy;
- dx = xx2 - mx;
- dy = yy2 - my;
- d2 = dx*dx + dy*dy;
-
- // if curve is flat enough, or no more divisions allowed then
- // add the straight line segment
- if (p2 - p1 <= 1 || (d1 <= flat && d2 <= flat)) {
- DB(printf(" (%g,%g)",xr3,yr3);)
- poly.push_back(Vertex<double>(xr3, yr3));
- p1 = p2;
-
- // otherwise, subdivide the curve
- } else {
- xl1 = (xl0 + xx1) * 0.5;
- yl1 = (yl0 + yy1) * 0.5;
- xh = (xx1 + xx2) * 0.5;
- yh = (yy1 + yy2) * 0.5;
- xl2 = (xl1 + xh) * 0.5;
- yl2 = (yl1 + yh) * 0.5;
- xr2 = (xx2 + xr3) * 0.5;
- yr2 = (yy2 + yr3) * 0.5;
- xr1 = (xh + xr2) * 0.5;
- yr1 = (yh + yr2) * 0.5;
- xr0 = (xl2 + xr1) * 0.5;
- yr0 = (yl2 + yr1) * 0.5;
-
- // add the new subdivision points
- p3 = (p1 + p2) / 2;
- x[p1][1] = xl1; y[p1][1] = yl1;
- x[p1][2] = xl2; y[p1][2] = yl2;
- next[p1] = p3;
- x[p3][0] = xr0; y[p3][0] = yr0;
- x[p3][1] = xr1; y[p3][1] = yr1;
- x[p3][2] = xr2; y[p3][2] = yr2;
- next[p3] = p2;
- }
- }
- }
-
- void AOutputDev::beginString(GfxState *state, GString *s) {
- text->beginString(state, s, font ? font->isHex() : gFalse);
- }
-
- void AOutputDev::endString(GfxState *state) {
- text->endString();
- }
-
- void AOutputDev::drawChar(GfxState *state, double x, double y,
- double dx, double dy, Guchar c) {
- Gushort c1;
- char *p;
- int n, i;
- double x1, y1;
- double tx;
-
- text->addChar(state, x, y, dx, dy, c);
-
- if (!font || c==13)
- return;
-
- // check for invisible text -- this is used by Acrobat Capture
- if ((state->getRender() & 3) == 3)
- return;
-
- DB(if(c>=32)printf("%c",c);else printf(" char(%x) ",c);)
- if (state->getRender() & 1) {
- if (curColor != strokeColor) {
- curColor = strokeColor;
- gfx.setapen(curColor);
- }
- } else {
- if (curColor != fillColor) {
- curColor = fillColor;
- gfx.setapen(curColor);
- }
- }
-
- state->transform(x, y, &x1, &y1);
- c1 = font->mapChar(c);
- if (c1 <= lastRegularChar)
- gfx.addchar(xoutRound(x1), xoutRound(y1), c1);
- else if (c1 <= lastSubstChar) {
- char c = (char)substChars[c1 - firstSubstChar];
- gfx.addchar(xoutRound(x1), xoutRound(y1), c);
- } else if (c1 <= lastConstrChar) {
- //~ need to deal with rotated text here
- switch (c1 - firstConstrChar) {
- case 0: // bullet
- tx = 0.25 * state->getTransformedFontSize() * gfxFont->getWidth(c);
- y1 -= 0.3 * state->getTransformedFontSize() * gfxFont->getWidth(c);
- //***y1 -= 0.4 * font->getTextFont()->tf_Baseline;
- gfx.rectfill(xoutRound(x1 + tx), xoutRound(y1 - tx),
- xoutRound(x1 + 3 * tx), xoutRound(y1 + tx));
- break;
- case 1: // trademark
- //~ this should use a smaller font
- // tx = state->getTransformedFontSize() *
- // (gfxFont->getWidth(c) -
- // gfxFont->getWidth(font->revCharMap('M')));
- tx = 0.9 * state->getTransformedFontSize() *
- gfxFont->getWidth(font->revMapChar('T'));
- y1 -= 0.33 * state->getTransformedFontSize() *
- gfxFont->getWidth(font->revMapChar('T'));
- //***y1 -= 0.33 * (double)font->getTextFont()->tf_Baseline;
- gfx.addchar(xoutRound(x1), xoutRound(y1), 'T');
- x1 += tx;
- gfx.addchar(xoutRound(x1), xoutRound(y1), 'M');
- break;
- }
- } else if (c1 <= lastMultiChar) {
- p = multiChars[c1 - firstMultiChar];
- n = strlen(p);
- tx = gfxFont->getWidth(c);
- tx -= gfxFont->getWidth(font->revMapChar(p[n-1]));
- tx = tx * state->getTransformedFontSize() / (double)(n - 1);
- for (i = 0; i < n; ++i) {
- gfx.addchar(xoutRound(x1), xoutRound(y1), p[i]);
- x1 += tx;
- }
- }
- }
-
- void AOutputDev::drawChar16(GfxState *state, double x, double y,
- double dx, double dy, int c) {
- DB(printf("\ndrawchar16\n");)
- }
-
- // These four functions are defined to work around a bug
- // in egcs for 68k: it gets confused when trying to inline
- // calls to Read/WritePixelArrays(8).
- void getimage(AGfx& gfx,AGfx::Image& im,short x,short y) {
- gfx.get_image(im, x, y);
- }
-
- void getimage(AGfx& gfx,AGfx::Image24& im,short x,short y) {
- gfx.get_image(im, x, y);
- }
-
- void putimage(AGfx& gfx,AGfx::Image& im,short x,short y) {
- gfx.put_image(im, x, y);
- }
-
- void putimage(AGfx& gfx,AGfx::Image24& im,short x,short y) {
- gfx.put_image(im, x, y);
- }
-
- void AOutputDev::drawImageMask(GfxState *state, Stream *str,
- int width, int height, GBool invert,
- GBool inlineImg) {
- int x0, y0; // top left corner of image
- int w0, h0, w1, h1; // size of image
- //int x2, y2;
- //int w2, h2;
- double xt, yt, wt, ht;
- GBool rotate, xFlip, yFlip;
- int x, y;
- int ix, iy;
- int px1, px2, qx, dx;
- int py1, py2, qy, dy;
- Guchar pixBuf;
- int i, j;
-
- // get image position and size
- state->transform(0, 0, &xt, &yt);
- state->transformDelta(1, 1, &wt, &ht);
- if (wt > 0) {
- x0 = xoutRound(xt);
- w0 = xoutRound(wt);
- } else {
- x0 = xoutRound(xt + wt);
- w0 = xoutRound(-wt);
- }
- if (ht > 0) {
- y0 = xoutRound(yt);
- h0 = xoutRound(ht);
- } else {
- y0 = xoutRound(yt + ht);
- h0 = xoutRound(-ht);
- }
- state->transformDelta(1, 0, &xt, &yt);
- rotate = fabs(xt) < fabs(yt);
- if (rotate) {
- w1 = h0;
- h1 = w0;
- xFlip = ht < 0;
- yFlip = wt > 0;
- } else {
- w1 = w0;
- h1 = h0;
- xFlip = wt < 0;
- yFlip = ht > 0;
- }
-
- // check for tiny (zero width or height) images
- if (w0 == 0 || h0 == 0) {
- j = height * ((width + 7) / 8);
- str->reset();
- for (i = 0; i < j; ++i)
- str->getChar();
- return;
- }
-
- // Bresenham parameters
- px1 = w1 / width;
- px2 = w1 - px1 * width;
- py1 = h1 / height;
- py2 = h1 - py1 * height;
-
- // initialize the image stream
- str->resetImage(width, 1, 1);
-
- // first line (column)
- y = yFlip ? h1 - 1 : 0;
- qy = 0;
-
- if(trueColor) {
- Gulong rgb = colorTable.find_rgb(fillColor);
- Color color;
- color.r = UBYTE(rgb >> 16);
- color.g = UBYTE(rgb >> 8);
- color.b = UBYTE(rgb);
-
- // allocate XImage
- AGfx::Image24Ptr image(gfx.allocate_image24(w0,h0));
- /*if (x0 + w0 > pixmapW)
- w2 = pixmapW - x0;
- else
- w2 = w0;
- if (x0 < 0) {
- x2 = -x0;
- w2 += x0;
- x0 = 0;
- } else {
- x2 = 0;
- }
- if (y0 + h0 > pixmapH)
- h2 = pixmapH - y0;
- else
- h2 = h0;
- if (y0 < 0) {
- y2 = -y0;
- h2 += y0;
- y0 = 0;
- } else {
- y2 = 0;
- }
- XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
- image, x2, y2);*/
- getimage(gfx, *image, x0, y0);
-
- // read image
- for (i = 0; i < height; ++i) {
-
- // vertical (horizontal) Bresenham
- dy = py1;
- if ((qy += py2) >= height) {
- ++dy;
- qy -= height;
- }
-
- // drop a line (column)
- if (dy == 0) {
- str->skipImageLine();
-
- } else {
-
- // first column (line)
- x = xFlip ? w1 - 1 : 0;
- qx = 0;
-
- // for each column (line)...
- for (j = 0; j < width; ++j) {
-
- // horizontal (vertical) Bresenham
- dx = px1;
- if ((qx += px2) >= width) {
- ++dx;
- qx -= width;
- }
-
- // get image pixel
- str->getImagePixel(&pixBuf);
- if (invert)
- pixBuf ^= 1;
-
- // draw image pixel
- if (dx > 0 && pixBuf == 0) {
- if (dx == 1 && dy == 1) {
- if (rotate)
- image->put(y, x, color);
- else
- image->put(x, y, color);
- } else {
- for (ix = 0; ix < dx; ++ix) {
- for (iy = 0; iy < dy; ++iy) {
- if (rotate)
- image->put(yFlip ? y - iy : y + iy,
- xFlip ? x - ix : x + ix, color);
- else
- image->put(xFlip ? x - ix : x + ix,
- yFlip ? y - iy : y + iy, color);
- }
- }
- }
- }
-
- // next column (line)
- if (xFlip)
- x -= dx;
- else
- x += dx;
- }
- }
-
- // next line (column)
- if (yFlip)
- y -= dy;
- else
- y += dy;
- }
-
- // blit the image into the pixmap
- DB(printf("\nimagemask24(%d,%d)->(%d,%d,%d,%d)\n",width,height,x0,y0,w0,h0);)
- putimage(gfx, *image, x0, y0);
- } else {
- // Inefficient hack. I should add a get_color() memberin AGfx...
- Gulong color;
- {
- AGfx::ColorTable table(1);
- ColorEntry* p=table.data();
- Gulong rgb = colorTable.find_rgb(fillColor);
- p->color.r = UBYTE(rgb >> 16) * 0x01010101;
- p->color.g = UBYTE(rgb >> 8) * 0x01010101;
- p->color.b = UBYTE(rgb) * 0x01010101;
- gfx.get_colors(table,1);
- color = table.data()->index;
- }
-
- // allocate XImage
- AGfx::ImagePtr image(gfx.allocate_image(w0,h0));
- /*if (x0 + w0 > pixmapW)
- w2 = pixmapW - x0;
- else
- w2 = w0;
- if (x0 < 0) {
- x2 = -x0;
- w2 += x0;
- x0 = 0;
- } else {
- x2 = 0;
- }
- if (y0 + h0 > pixmapH)
- h2 = pixmapH - y0;
- else
- h2 = h0;
- if (y0 < 0) {
- y2 = -y0;
- h2 += y0;
- y0 = 0;
- } else {
- y2 = 0;
- }
- XGetSubImage(display, pixmap, x0, y0, w2, h2, (1 << depth) - 1, ZPixmap,
- image, x2, y2);*/
- getimage(gfx, *image, x0, y0);
-
- // read image
- for (i = 0; i < height; ++i) {
-
- // vertical (horizontal) Bresenham
- dy = py1;
- if ((qy += py2) >= height) {
- ++dy;
- qy -= height;
- }
-
- // drop a line (column)
- if (dy == 0) {
- str->skipImageLine();
-
- } else {
-
- // first column (line)
- x = xFlip ? w1 - 1 : 0;
- qx = 0;
-
- // for each column (line)...
- for (j = 0; j < width; ++j) {
-
- // horizontal (vertical) Bresenham
- dx = px1;
- if ((qx += px2) >= width) {
- ++dx;
- qx -= width;
- }
-
- // get image pixel
- str->getImagePixel(&pixBuf);
- if (invert)
- pixBuf ^= 1;
-
- // draw image pixel
- if (dx > 0 && pixBuf == 0) {
- if (dx == 1 && dy == 1) {
- if (rotate)
- image->put(y, x, color);
- else
- image->put(x, y, color);
- } else {
- for (ix = 0; ix < dx; ++ix) {
- for (iy = 0; iy < dy; ++iy) {
- if (rotate)
- image->put(yFlip ? y - iy : y + iy,
- xFlip ? x - ix : x + ix, color);
- else
- image->put(xFlip ? x - ix : x + ix,
- yFlip ? y - iy : y + iy, color);
- }
- }
- }
- }
-
- // next column (line)
- if (xFlip)
- x -= dx;
- else
- x += dx;
- }
- }
-
- // next line (column)
- if (yFlip)
- y -= dy;
- else
- y += dy;
- }
-
- // blit the image into the pixmap
- DB(printf("\nimagemask(%d,%d)->(%d,%d,%d,%d)\n",width,height,x0,y0,w0,h0);)
- putimage(gfx,*image, x0, y0);
- }
- }
-
- #if 1
-
- // Color quantization code, to display true color embedded pictures
- // on colormap screens.
- // Not sure where I got that algorithm from. Maybe the newsgroup
- // comp.graphics.algorithm, maybe Xv, maybe somewhere else...
-
- struct ColorDiff32 {
- LONG r, g, b;
- };
-
- struct col_hist {
- Color color;
- int value;
- };
-
- static col_hist* get_color_hist(const Color* p,int size,int maxcols,int& hist_size,UBYTE mask) {
-
- class hash_table {
- enum { hash_table_size = 6553 };
- struct node {
- node* next;
- col_hist ch;
- };
- static bool equal(const Color& c1,const Color& c2,UBYTE mask) {
- return (((c1.r^c2.r)|(c1.g^c2.g)|(c1.b^c2.b))&mask)==0;
- }
- static int hash(const Color& c, UBYTE m) {
- return ((c.r & m) * 33023 +
- (c.g & m) * 30013 +
- (c.b & m) * 27011) % hash_table_size;
- }
- public:
- class iterator {
- friend class hash_table;
- public:
- bool operator == (const iterator& i) const { return q == i.q; }
- bool operator != (const iterator& i) const { return q != i.q; }
- col_hist& operator * () const { return q->ch; }
- iterator& operator ++ () {
- q=q->next;
- if(!q) {
- while (++p != end && !*p);
- if (p != end)
- q=*p;
- }
- return *this;
- }
- private:
- node** p;
- node* q;
- node** end;
- };
-
- hash_table() : nodes(new node* [hash_table_size]),sz(0) {
- memset(nodes,0,sizeof(*nodes)*hash_table_size);
- }
- ~hash_table() {
- for(int k = 0; k < hash_table_size; ++k) {
- node* p = nodes[k];
- while(p) {
- node* q = p->next;
- delete p;
- p = q;
- }
- }
- delete [] nodes;
- }
-
- int size() const { return sz; }
-
- iterator begin() {
- iterator i;
- i.p = nodes;
- i.end = nodes + hash_table_size;
- while (!*i.p && ++i.p != i.end);
- if (i.p != i.end)
- i.q = *i.p;
- else
- i.q = NULL;
- return i;
- }
-
- iterator end() {
- iterator i;
- i.q = NULL;
- return i;
- }
-
- col_hist& get(const Color& c,UBYTE mask) {
- int h = hash(c, mask);
- for(node* hn = nodes[h]; hn; hn = hn->next)
- if(equal(c, hn->ch.color, mask))
- return hn->ch;
- ++sz;
- node* hn = new node;
- hn->ch.color = c;
- hn->ch.value = 0;
- hn->next = nodes[h];
- nodes[h] = hn;
- return hn->ch;
- }
- private:
- node** nodes;
- int sz;
- };
-
-
- hash_table ht;
-
- while (--size >= 0) {
- col_hist& hn = ht.get(*p++, mask);
- ++hn.value;
- if (ht.size() > maxcols)
- return NULL;
- }
-
- hist_size = ht.size();
- col_hist* ch = new col_hist [hist_size];
- col_hist* q = ch;
- hash_table::iterator e(ht.end());
- for(hash_table::iterator i(ht.begin()); i != e; ++i)
- *q++ = *i;
-
- return ch;
- }
-
- static int redcmp(const void *p1, const void *p2) {
- const col_hist *ch1 = (const col_hist *)p1;
- const col_hist *ch2 = (const col_hist *)p2;
- return ch1->color.r - ch2->color.r;
- }
- static int greencmp(const void *p1, const void *p2) {
- const col_hist *ch1 = (const col_hist *)p1;
- const col_hist *ch2 = (const col_hist *)p2;
- return ch1->color.g - ch2->color.g;
- }
- static int bluecmp(const void *p1, const void *p2) {
- const col_hist *ch1 = (const col_hist *)p1;
- const col_hist *ch2 = (const col_hist *)p2;
- return ch1->color.b - ch2->color.b;
- }
-
-
- static bool median_cut(ColorEntry* cm, col_hist* ch, int colors, long sum, int newcolors) {
- struct box {
- int index;
- int colors;
- long sum;
- };
-
- box* boxes = new box [newcolors];
- /*
- * Start with 1 big box
- */
- int nboxes = 1;
- boxes[0].index = 0;
- boxes[0].colors = colors;
- boxes[0].sum = sum;
-
- /*
- * Split boxes
- */
-
- while (nboxes < newcolors) {
- int bi;
- UBYTE minr, maxr, ming, maxg, minb, maxb;
-
- /*
- * Find the first splitable box. Boxes are sorted by sums.
- */
- for(bi = 0; bi < nboxes && boxes[bi].colors < 2; bi++);
- if (bi == nboxes)
- break;
-
- int indx = boxes[bi].index;
- int clrs = boxes[bi].colors;
- long sm = boxes[bi].sum;
-
- /*
- * Find the boundaries of the box
- */
-
- minr = maxr = ch[indx].color.r;
- ming = maxg = ch[indx].color.g;
- minb = maxb = ch[indx].color.b;
-
- for(int i = 1; i < clrs; ++i) {
- UBYTE v = ch[i].color.r;
- if (v < minr) minr = v; else if (v > maxr) maxr = v;
- v = ch[i].color.g;
- if (v < ming) ming = v; else if (v > maxg) maxg = v;
- v = ch[i].color.b;
- if (v < minb) minb = v; else if (v > maxb) maxb = v;
- }
-
- /*
- * Find the largest dimension, and sort the colors in the box by
- * this component.
- */
- int rl = 77 * (maxr - minr);
- int gl = 150 * (maxg - ming);
- int bl = 29 * (maxb - minb);
-
- if (rl >= gl && rl >= bl)
- qsort(&ch[indx], clrs, sizeof(col_hist), redcmp);
- else if (gl >= bl)
- qsort(&ch[indx], clrs, sizeof(col_hist), greencmp);
- else
- qsort(&ch[indx], clrs, sizeof(col_hist), bluecmp);
-
- /*
- * Split the box.
- */
-
- long lsum = ch[indx].value;
- long hsum = sm / 2;
- int i;
- for (i = 1; i < clrs-1; i++) {
- if (lsum >= hsum)
- break;
- lsum += ch[indx+i].value;
- }
- sm -= lsum;
-
- /*
- * Add the two boxes in the right order
- */
- boxes[nboxes].sum=0;
- int a;
- for(a = bi+1; boxes[a].sum > lsum; ++a);
- --a;
- if (a != bi)
- memmove(&boxes[bi], &boxes[bi+1], (a-bi) * sizeof(box));
- boxes[a].index = indx;
- boxes[a].colors = i;
- boxes[a].sum = lsum;
- for (++a; boxes[a].sum > sm; ++a);
- if (a != nboxes)
- memmove(&boxes[a+1], &boxes[a], (nboxes-a) * sizeof(box));
- boxes[a].index = indx + i;
- boxes[a].colors = clrs - i;
- boxes[a].sum = sm;
- ++nboxes;
- }
-
- /*
- * Find a representative color for each box.
- */
-
- for (int i = 0; i < nboxes; ++i) {
- long s=0, r=0, g=0, b=0;
- col_hist* p = &ch[boxes[i].index];
-
- for (int j = 0; j < boxes[i].colors; ++j, ++p) {
- r += p->color.r * p->value;
- g += p->color.g * p->value;
- b += p->color.b * p->value;
- s += p->value;
- }
- r /= s;
- g /= s;
- b /= s;
- cm[i].color.r = r | (r << 8) | (r << 16) | (r << 24);
- cm[i].color.g = g | (g << 8) | (g << 16) | (g << 24);
- cm[i].color.b = b | (b << 8) | (b << 16) | (b << 24);
- }
-
- delete [] boxes;
- return true;
- }
-
-
-
-
- class hash_table {
- enum { hash_table_size = 6553 };
- struct node {
- node* next;
- ColorDiff32 color;
- ColorDiff32 err;
- int index;
- };
- static int hash(const ColorDiff32& c) {
- return ((c.r >> 8) * 33023 +
- (c.g >> 8) * 30013 +
- (c.b >> 8) * 27011) % hash_table_size;
- }
- static ULONG sq(LONG x) { return ULONG(x * x) >> 4; }
- public:
- hash_table(ColorEntry* t, int n)
- : nodes(new node* [hash_table_size]), table(t), colors(n) {
- memset(nodes,0,sizeof(*nodes)*hash_table_size);
- }
- ~hash_table() {
- for(int k = 0; k < hash_table_size; ++k) {
- node* p = nodes[k];
- while(p) {
- node* q = p->next;
- delete p;
- p = q;
- }
- }
- delete [] nodes;
- }
-
- int get(const ColorDiff32& c, ColorDiff32& err) {
- int h = hash(c);
- for(node* hn = nodes[h]; hn; hn = hn->next)
- if((((hn->color.r ^ c.r) |
- (hn->color.g ^ c.g) |
- (hn->color.b ^ c.b)) & 0xff00) == 0) {
- err = hn->err;
- return hn->index;
- }
-
- ULONG d = 0xffffffffL;
- ColorEntry* q = table;
- ColorEntry* e = q;
- for (int k = 0; k < colors; ++k, ++q) {
- ULONG d2 = sq(c.r - (q->color.r >> 16)) +
- sq(c.g - (q->color.g >> 16)) +
- sq(c.b - (q->color.b >> 16));
- if (d2 < d) {
- d = d2;
- e = q;
- }
- }
- err.r = c.r - (e->color.r >> 16);
- err.g = c.g - (e->color.g >> 16);
- err.b = c.b - (e->color.b >> 16);
- node* hn = new node;
- hn->color = c;
- hn->err = err;
- hn->index = e->index;
- hn->next = nodes[h];
- nodes[h] = hn;
- return e->index;
- }
- private:
- node** nodes;
- int colors;
- ColorEntry* table;
- };
-
-
-
- void AOutputDev::drawImage(GfxState *state, Stream *str, int width,
- int height, GfxImageColorMap *colorMap,
- GBool inlineImg) {
- int x0, y0; // top left corner of image
- int w0, h0, w1, h1; // size of image
- double xt, yt, wt, ht;
- GBool rotate, xFlip, yFlip;
- GBool dither;
- int x, y;
- int ix, iy;
- int px1, px2, qx, dx;
- int py1, py2, qy, dy;
- Guchar pixBuf[4];
- int nComps, nVals, nBits;
- int i, j;
-
- // get image position and size
- state->transform(0, 0, &xt, &yt);
- state->transformDelta(1, 1, &wt, &ht);
- if (wt > 0) {
- x0 = xoutRound(xt);
- w0 = xoutRound(wt);
- } else {
- x0 = xoutRound(xt + wt);
- w0 = xoutRound(-wt);
- }
- if (ht > 0) {
- y0 = xoutRound(yt);
- h0 = xoutRound(ht);
- } else {
- y0 = xoutRound(yt + ht);
- h0 = xoutRound(-ht);
- }
- state->transformDelta(1, 0, &xt, &yt);
- rotate = fabs(xt) < fabs(yt);
- if (rotate) {
- w1 = h0;
- h1 = w0;
- xFlip = ht < 0;
- yFlip = wt > 0;
- } else {
- w1 = w0;
- h1 = h0;
- xFlip = wt < 0;
- yFlip = ht > 0;
- }
-
- DB(printf("image(%dx%d)->(%d,%d)\n",width,height,w0,h0);)
- // set up
- nComps = colorMap->getNumPixelComps();
- nVals = width * nComps;
- nBits = colorMap->getBits();
- dither = nComps > 1 || nBits > 1;
-
- // check for tiny (zero width or height) images
- if (w0 == 0 || h0 == 0) {
- j = height * ((nVals * nBits + 7) / 8);
- str->reset();
- for (i = 0; i < j; ++i)
- str->getChar();
- return;
- }
-
- // Bresenham parameters
- px1 = w1 / width;
- px2 = w1 - px1 * width;
- py1 = h1 / height;
- py2 = h1 - py1 * height;
-
- // allocate the true color image
- AGfx::Image24Ptr image24(gfx.allocate_image24(w0, h0));
-
- // initialize the image stream
- str->resetImage(width, nComps, nBits);
-
- // first line (column)
- y = yFlip ? h1 - 1 : 0;
- qy = 0;
-
- // read image
- for (i = 0; i < height; ++i) {
-
- // vertical (horizontal) Bresenham
- dy = py1;
- if ((qy += py2) >= height) {
- ++dy;
- qy -= height;
- }
-
- // drop a line (column)
- if (dy == 0) {
- str->skipImageLine();
-
- } else {
-
- // first column (line)
- x = xFlip ? w1 - 1 : 0;
- qx = 0;
-
- // for each column (line)...
- for (j = 0; j < width; ++j) {
-
- // horizontal (vertical) Bresenham
- dx = px1;
- if ((qx += px2) >= width) {
- ++dx;
- qx -= width;
- }
-
- // get image pixel
- str->getImagePixel(pixBuf);
- GfxColor color;
- colorMap->getColor(pixBuf, &color);
- Color c;
- c.r = (UBYTE)(255 * color.getR());
- c.g = (UBYTE)(255 * color.getG());
- c.b = (UBYTE)(255 * color.getB());
-
- // draw image pixel
- if (dx > 0) {
- for (iy = 0; iy < dy; ++iy) {
- for (ix = 0; ix < dx; ++ix) {
- if (rotate)
- image24->put(yFlip ? y - iy : y + iy,
- xFlip ? x - ix : x + ix, c);
- else
- image24->put(xFlip ? x - ix : x + ix,
- yFlip ? y - iy : y + iy, c);
- }
- }
- }
-
- // next column (line)
- if (xFlip)
- x -= dx;
- else
- x += dx;
- }
- }
-
- // next line (column)
- if (yFlip)
- y -= dy;
- else
- y += dy;
- }
-
- if (trueColor) {
- putimage(gfx,*image24, x0, y0);
- DB(printf("\nimage24(%d,%d)->(%d,%d,%d,%d)\n",width,height,x0,y0,w0,h0);)
- return;
- }
-
- // compute color histogram
- const int max_colors = 32767;
-
- int colors;
- UBYTE mask = 255;//...
- int newColors = maxColors<2?2:maxColors;
- col_hist* ch;
- do {
- ch = get_color_hist(image24->data(), w0*h0, max_colors, colors, mask);
- mask = ~((((~mask)+1)<<1)-1);
- } while (!ch);
-
- // find the prefered colors
- if (colors < newColors)
- newColors = colors;
- AGfx::ColorTablePtr colorTable2(gfx.allocate_color_table(newColors));
- median_cut(colorTable2->data(), ch, colors, w0*h0, newColors);
-
- // allocate those colors
- gfx.get_colors(*colorTable2, newColors);
- {
- ColorEntry* p = colorTable2->data();
- for(int k = 0; k < newColors; ++k, ++p)
- colorTable.add(((p->color.r >> 8) & 0xff0000)|
- ((p->color.g >> 16) & 0x00ff00) |
- ((p->color.b >> 24) & 0x0000ff));
- }
-
- // allocate image
- AGfx::ImagePtr image(gfx.allocate_image(w0, h0));
-
- // allocate error diffusion accumulators
- ColorDiff32 errRight, *errDown;
- if (dither) {
- errDown = new ColorDiff32 [w0];
- memset(errDown, 0, w0*sizeof(Color32));
- } else {
- errDown = NULL;
- }
-
- hash_table table(colorTable2->data(), newColors);
-
- // first line
- Color* p = image24->data();
-
- // read image
- for (y = 0; y < h0; ++y) {
-
- // clear error accumulator
- errRight.r = errRight.g = errRight.b = 0;
-
- // for each column (line)...
- for (x = 0; x < w0; ++x) {
-
- // draw image pixel
- ColorDiff32 color1;
- color1.r = LONG(p->r << 8);
- color1.g = LONG(p->g << 8);
- color1.b = LONG(p->b << 8);
- ++p;
-
- if (dither) {
- color1.r += errRight.r + errDown[x].r;
- if (color1.r > 0xffff)
- color1.r = 0xffff;
- else if (color1.r < 0)
- color1.r = 0;
- color1.g += errRight.g + errDown[x].g;
- if (color1.g > 0xffff)
- color1.g = 0xffff;
- else if (color1.g < 0)
- color1.g = 0;
- color1.b += errRight.b + errDown[x].b;
- if (color1.b > 0xffff)
- color1.b = 0xffff;
- else if (color1.b < 0)
- color1.b = 0;
- }
- ColorDiff32 err;
- image->put(x, y, table.get(color1, err));
- if (dither) {
- errRight.r = errDown[x].r = err.r / 2;
- errRight.g = errDown[x].g = err.g / 2;
- errRight.b = errDown[x].b = err.b / 2;
- }
- }
- }
-
- // blit the image into the pixmap
- putimage(gfx,*image, x0, y0/*, w0, h0*/);
- DB(printf("\nimage(%d,%d)->(%d,%d,%d,%d)\n",width,height,x0,y0,w0,h0);)
-
- // free memory
- delete [] errDown;
- }
- #else
-
- inline Gulong AOutputDev::findColor(RGBColor *x, RGBColor *err) {
- double gray;
- int r, g, b;
- Gulong pixel;
-
- /*if (trueColor) {
- r = xoutRound(x->r * rMul);
- g = xoutRound(x->g * gMul);
- b = xoutRound(x->b * bMul);
- pixel = ((Gulong)r << rShift) +
- ((Gulong)g << gShift) +
- ((Gulong)b << bShift);
- err->r = x->r - (double)r / rMul;
- err->g = x->g - (double)g / gMul;
- err->b = x->b - (double)b / bMul;
- } else if (numColors == 1) {*/
- gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
- if (gray < 0.5) {
- pixel = 0;//colors[0];
- err->r = x->r;
- err->g = x->g;
- err->b = x->b;
- } else {
- pixel = 1;//colors[1];
- err->r = x->r - 1;
- err->g = x->g - 1;
- err->b = x->b - 1;
- }
- /*} else {
- r = xoutRound(x->r * (numColors - 1));
- g = xoutRound(x->g * (numColors - 1));
- b = xoutRound(x->b * (numColors - 1));
- pixel = colors[(r * numColors + g) * numColors + b];
- err->r = x->r - (double)r / (numColors - 1);
- err->g = x->g - (double)g / (numColors - 1);
- err->b = x->b - (double)b / (numColors - 1);
- }*/
- return pixel;
- }
-
- void AOutputDev::drawImage(GfxState *state, Stream *str, int width,
- int height, GfxImageColorMap *colorMap,
- GBool inlineImg) {
- AGfx::ImagePtr image;
- int x0, y0; // top left corner of image
- int w0, h0, w1, h1; // size of image
- double xt, yt, wt, ht;
- GBool rotate, xFlip, yFlip;
- GBool dither;
- int x, y;
- int ix, iy;
- int px1, px2, qx, dx;
- int py1, py2, qy, dy;
- Guchar pixBuf[4];
- Gulong pixel;
- int nComps, nVals, nBits;
- double r1, g1, b1;
- GfxColor color;
- RGBColor color2, err;
- RGBColor *errRight, *errDown;
- int i, j;
-
- // get image position and size
- state->transform(0, 0, &xt, &yt);
- state->transformDelta(1, 1, &wt, &ht);
- if (wt > 0) {
- x0 = xoutRound(xt);
- w0 = xoutRound(wt);
- } else {
- x0 = xoutRound(xt + wt);
- w0 = xoutRound(-wt);
- }
- if (ht > 0) {
- y0 = xoutRound(yt);
- h0 = xoutRound(ht);
- } else {
- y0 = xoutRound(yt + ht);
- h0 = xoutRound(-ht);
- }
- state->transformDelta(1, 0, &xt, &yt);
- rotate = fabs(xt) < fabs(yt);
- if (rotate) {
- w1 = h0;
- h1 = w0;
- xFlip = ht < 0;
- yFlip = wt > 0;
- } else {
- w1 = w0;
- h1 = h0;
- xFlip = wt < 0;
- yFlip = ht > 0;
- }
-
- // set up
- nComps = colorMap->getNumPixelComps();
- nVals = width * nComps;
- nBits = colorMap->getBits();
- dither = nComps > 1 || nBits > 1;
-
- // check for tiny (zero width or height) images
- if (w0 == 0 || h0 == 0) {
- j = height * ((nVals * nBits + 7) / 8);
- str->reset();
- for (i = 0; i < j; ++i)
- str->getChar();
- return;
- }
-
- // Bresenham parameters
- px1 = w1 / width;
- px2 = w1 - px1 * width;
- py1 = h1 / height;
- py2 = h1 - py1 * height;
-
- // allocate XImage
- /*
- image = XCreateImage(display, DefaultVisual(display, screenNum),
- depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
- image->data = (char *)gmalloc(h0 * image->bytes_per_line);*/
- image = gfx.allocate_image(w0,h0);
-
- // allocate error diffusion accumulators
- if (dither) {
- errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
- errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
- for (j = 0; j < w1; ++j)
- errDown[j].r = errDown[j].g = errDown[j].b = 0;
- } else {
- errDown = NULL;
- errRight = NULL;
- }
-
- // initialize the image stream
- str->resetImage(width, nComps, nBits);
-
- // first line (column)
- y = yFlip ? h1 - 1 : 0;
- qy = 0;
-
- // read image
- for (i = 0; i < height; ++i) {
-
- // vertical (horizontal) Bresenham
- dy = py1;
- if ((qy += py2) >= height) {
- ++dy;
- qy -= height;
- }
-
- // drop a line (column)
- if (dy == 0) {
- str->skipImageLine();
-
- } else {
-
- // first column (line)
- x = xFlip ? w1 - 1 : 0;
- qx = 0;
-
- // clear error accumulator
- if (dither) {
- for (j = 0; j <= py1; ++j)
- errRight[j].r = errRight[j].g = errRight[j].b = 0;
- }
-
- // for each column (line)...
- for (j = 0; j < width; ++j) {
-
- // horizontal (vertical) Bresenham
- dx = px1;
- if ((qx += px2) >= width) {
- ++dx;
- qx -= width;
- }
-
- // get image pixel
- str->getImagePixel(pixBuf);
-
- // draw image pixel
- if (dx > 0) {
- colorMap->getColor(pixBuf, &color);
- r1 = color.getR();
- g1 = color.getG();
- b1 = color.getB();
- if (dither) {
- pixel = 0;
- } else {
- color2.r = r1;
- color2.g = g1;
- color2.b = b1;
- pixel = findColor(&color2, &err);
- }
- if (dx == 1 && dy == 1) {
- if (dither) {
- color2.r = r1 + errRight[0].r + errDown[x].r;
- if (color2.r > 1)
- color2.r = 1;
- else if (color2.r < 0)
- color2.r = 0;
- color2.g = g1 + errRight[0].g + errDown[x].g;
- if (color2.g > 1)
- color2.g = 1;
- else if (color2.g < 0)
- color2.g = 0;
- color2.b = b1 + errRight[0].b + errDown[x].b;
- if (color2.b > 1)
- color2.b = 1;
- else if (color2.b < 0)
- color2.b = 0;
- pixel = findColor(&color2, &err);
- errRight[0].r = errDown[x].r = err.r / 2;
- errRight[0].g = errDown[x].g = err.g / 2;
- errRight[0].b = errDown[x].b = err.b / 2;
- }
- if (rotate)
- image->put(y, x, pixel);
- else
- image->put(x, y, pixel);
- } else {
- for (iy = 0; iy < dy; ++iy) {
- for (ix = 0; ix < dx; ++ix) {
- if (dither) {
- color2.r = r1 + errRight[iy].r +
- errDown[xFlip ? x - ix : x + ix].r;
- if (color2.r > 1)
- color2.r = 1;
- else if (color2.r < 0)
- color2.r = 0;
- color2.g = g1 + errRight[iy].g +
- errDown[xFlip ? x - ix : x + ix].g;
- if (color2.g > 1)
- color2.g = 1;
- else if (color2.g < 0)
- color2.g = 0;
- color2.b = b1 + errRight[iy].b +
- errDown[xFlip ? x - ix : x + ix].b;
- if (color2.b > 1)
- color2.b = 1;
- else if (color2.b < 0)
- color2.b = 0;
- pixel = findColor(&color2, &err);
- errRight[iy].r = errDown[xFlip ? x - ix : x + ix].r =
- err.r / 2;
- errRight[iy].g = errDown[xFlip ? x - ix : x + ix].g =
- err.g / 2;
- errRight[iy].b = errDown[xFlip ? x - ix : x + ix].b =
- err.b / 2;
- }
- if (rotate)
- image->put(yFlip ? y - iy : y + iy,
- xFlip ? x - ix : x + ix, pixel);
- else
- image->put(xFlip ? x - ix : x + ix,
- yFlip ? y - iy : y + iy, pixel);
- }
- }
- }
- }
-
- // next column (line)
- if (xFlip)
- x -= dx;
- else
- x += dx;
- }
- }
-
- // next line (column)
- if (yFlip)
- y -= dy;
- else
- y += dy;
- }
-
- // blit the image into the pixmap
- gfx.put_image(*image, x0, y0/*, w0, h0*/);
- DB(printf("\nimage(%d,%d)->(%d,%d,%d,%d)\n",width,height,x0,y0,w0,h0);)
-
- // free memory
- gfree(errRight);
- gfree(errDown);
- }
- #endif
-
- Gulong AOutputDev::findColor(GfxColor *color) {
- int r, g, b;
- Gulong pixel;
-
- #if !defined(__HAVE_68881__) && !defined(__PPC__)
- // when compiling for 68k without fpu, I get r==256 if
- // getR()==1.0, which is wrong. Unfortunately have been
- // unable to reproduce the problem on a small snipset...
- r = int(color->getR() * 255);
- g = int(color->getG() * 255);
- b = int(color->getB() * 255);
- #else
- r = xoutRound(color->getR() * 255);
- g = xoutRound(color->getG() * 255);
- b = xoutRound(color->getB() * 255);
- #endif
-
- pixel = (((Gulong)r << 16) +
- ((Gulong)g << 8) +
- ((Gulong)b << 0)) & colorMask;
-
- Gulong c = colorTable.find(pixel);
- if (c != Gulong(-1))
- return c;
- else {
- gfx.color(Gulong(color->getR() * 0xffffffffUL + 0.5),
- Gulong(color->getG() * 0xffffffffUL + 0.5),
- Gulong(color->getB() * 0xffffffffUL + 0.5));
- return colorTable.add(pixel);
- }
- #if 0
- double gray;
-
- if (trueColor) {
- } else if (numColors == 1) {
- gray = color->getGray();
- if (gray < 0.5)
- pixel = colors[0];
- else
- pixel = colors[1];
- } else {
- r = xoutRound(color->getR() * (numColors - 1));
- g = xoutRound(color->getG() * (numColors - 1));
- b = xoutRound(color->getB() * (numColors - 1));
- #if 0 //~ this makes things worse as often as better
- // even a very light color shouldn't map to white
- if (r == numColors - 1 && g == numColors - 1 && b == numColors - 1) {
- if (color->getR() < 0.95)
- --r;
- if (color->getG() < 0.95)
- --g;
- if (color->getB() < 0.95)
- --b;
- }
- #endif
- pixel = colors[(r * numColors + g) * numColors + b];
- }
- return pixel;
- #endif
- }
-
- GBool AOutputDev::findText(char *s, GBool top, GBool bottom,
- int *xMin, int *yMin, int *xMax, int *yMax) {
- double xMin1, yMin1, xMax1, yMax1;
-
- xMin1 = (double)*xMin;
- yMin1 = (double)*yMin;
- xMax1 = (double)*xMax;
- yMax1 = (double)*yMax;
- if (text->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
- *xMin = xoutRound(xMin1);
- *xMax = xoutRound(xMax1);
- *yMin = xoutRound(yMin1);
- *yMax = xoutRound(yMax1);
- return gTrue;
- }
- return gFalse;
- }
-
- GString *AOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
- return text->getText((double)xMin, (double)yMin,
- (double)xMax, (double)yMax);
- }
-