home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 110
/
EnigmaAmiga110CD.iso
/
indispensabili
/
utility
/
apdf
/
xpdf-0.80
/
xpdf
/
aoutputdev.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1999-06-10
|
57KB
|
2,358 lines
//========================================================================
//
// 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);
}