diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 6f2d4d6..66c6005 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -1210,7 +1210,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool bitmapTopDownA, - GBool allowAntialiasA) { + GBool allowAntialiasA, SplashThinLineMode thinLineMode) { colorMode = colorModeA; bitmapRowPad = bitmapRowPadA; bitmapTopDown = bitmapTopDownA; @@ -1238,6 +1238,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setThinLineMode(thinLineMode); splash->clear(paperColor, 0); fontEngine = NULL; @@ -1364,7 +1365,9 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { } else { w = h = 1; } + SplashThinLineMode thinLineMode = splashThinLineDefault; if (splash) { + thinLineMode = splash->getThinLineMode(); delete splash; splash = NULL; } @@ -1377,6 +1380,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { colorMode != splashModeMono1, bitmapTopDown); } splash = new Splash(bitmap, vectorAntialias, &screenParams); + splash->setThinLineMode(thinLineMode); splash->setMinLineWidth(globalParams->getMinLineWidth()); if (state) { ctm = state->getCTM(); @@ -1418,8 +1422,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) { splash->setFlatness(1); // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true - splash->setStrokeAdjust(globalParams->getStrokeAdjust()); - splash->clear(paperColor, 0); + splash->setStrokeAdjust(globalParams->getStrokeAdjust()); splash->clear(paperColor, 0); } void SplashOutputDev::endPage() { @@ -2581,6 +2584,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, color[0] = 0xff; } splash->setMinLineWidth(globalParams->getMinLineWidth()); + splash->setThinLineMode(splashThinLineDefault); splash->setFillPattern(new SplashSolidColor(color)); splash->setStrokePattern(new SplashSolidColor(color)); //~ this should copy other state from t3GlyphStack->origSplash? @@ -3816,6 +3820,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, bitmapTopDown, bitmap->getSeparationList()); splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); + splash->setThinLineMode(transpGroup->origSplash->getThinLineMode()); splash->setMinLineWidth(globalParams->getMinLineWidth()); //~ Acrobat apparently copies at least the fill and stroke colors, and //~ maybe other state(?) -- but not the clipping path (and not sure @@ -4203,6 +4208,7 @@ GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *ca #endif } splash = new Splash(bitmap, gTrue); + splash->setThinLineMode(formerSplash->getThinLineMode()); splash->setMinLineWidth(globalParams->getMinLineWidth()); box.x1 = bbox[0]; box.y1 = bbox[1]; diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index de7934d..373ed47 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -163,7 +163,8 @@ public: SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, GBool reverseVideoA, SplashColorPtr paperColorA, GBool bitmapTopDownA = gTrue, - GBool allowAntialiasA = gTrue); + GBool allowAntialiasA = gTrue, + SplashThinLineMode thinLineMode = splashThinLineDefault); // Destructor. virtual ~SplashOutputDev(); diff --git a/splash/Splash.cc b/splash/Splash.cc index 336872b..d0c7609 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -1450,7 +1450,7 @@ inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, } } -inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { +inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine, Guchar lineOpacity) { #if splashAASize == 4 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; @@ -1493,7 +1493,7 @@ inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { #endif if (t != 0) { - pipe->shape = aaGamma[t]; + pipe->shape = (adjustLine) ? div255((int) lineOpacity * aaGamma[t]) : aaGamma[t]; (this->*pipe->run)(pipe); updateModX(x); updateModY(y); @@ -1542,6 +1542,7 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, aaBuf = NULL; } minLineWidth = 0; + thinLineMode = splashThinLineDefault; clearModRegion(); debugMode = gFalse; } @@ -1568,6 +1569,7 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, aaBuf = NULL; } minLineWidth = 0; + thinLineMode = splashThinLineDefault; clearModRegion(); debugMode = gFalse; } @@ -2362,6 +2364,8 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashXPathScanner *scanner; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes, clipRes2; + GBool adjustLine = gFalse; + int linePosI = 0; if (path->length == 0) { return splashErrEmptyPath; @@ -2394,7 +2398,24 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, } } - xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + if (thinLineMode != splashThinLineDefault) { + if (state->clip->getXMinI() == state->clip->getXMaxI()) { + linePosI = state->clip->getXMinI(); + adjustLine = gTrue; + } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getXMin() + state->lineWidth); + } else if (state->clip->getYMinI() == state->clip->getYMaxI()) { + linePosI = state->clip->getYMinI(); + adjustLine = gTrue; + } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getYMin() + state->lineWidth); + } + } + + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue, + adjustLine, linePosI); if (vectorAntialias && !inShading) { xPath->aaScale(); } @@ -2427,11 +2448,13 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, // draw the spans if (vectorAntialias && !inShading) { for (y = yMinI; y <= yMaxI; ++y) { - scanner->renderAALine(aaBuf, &x0, &x1, y); + scanner->renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); if (clipRes != splashClipAllInside) { - state->clip->clipAALine(aaBuf, &x0, &x1, y); + state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); } - drawAALine(&pipe, x0, x1, y); + drawAALine(&pipe, x0, x1, y, + thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI), + clip255(splashRound(state->lineWidth * 255))); } } else { for (y = yMinI; y <= yMaxI; ++y) { diff --git a/splash/Splash.h b/splash/Splash.h index c31dbc8..4f43adf 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -248,6 +248,10 @@ public: // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } + // Setter/Getter for thin line mode + void setThinLineMode(SplashThinLineMode thinLineModeA) { thinLineMode = thinLineModeA; } + SplashThinLineMode getThinLineMode() { return thinLineMode; } + // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) @@ -307,7 +311,7 @@ private: void drawAAPixelInit(); void drawAAPixel(SplashPipe *pipe, int x, int y); void drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); - void drawAALine(SplashPipe *pipe, int x0, int x1, int y); + void drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine = gFalse, Guchar lineOpacity = 0); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); @@ -409,6 +413,7 @@ private: int alpha0X, alpha0Y; // offset within alpha0Bitmap SplashCoord aaGamma[splashAASize * splashAASize + 1]; SplashCoord minLineWidth; + SplashThinLineMode thinLineMode; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc index fb18831..aa51be5 100644 --- a/splash/SplashClip.cc +++ b/splash/SplashClip.cc @@ -334,7 +334,7 @@ SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { return splashClipAllInside; } -void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { +void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, GBool adjustVertLine) { int xx0, xx1, xx, yy, i; SplashColorPtr p; @@ -351,7 +351,7 @@ void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { for (xx = xx0; xx + 7 < xx1; xx += 8) { *p++ = 0; } - if (xx < xx1) { + if (xx < xx1 && !adjustVertLine) { *p &= 0xff >> (xx1 & 7); } } @@ -364,7 +364,7 @@ void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { xx0 = 0; } xx1 = (*x1 + 1) * splashAASize; - if (xx0 < xx1) { + if (xx0 < xx1 && !adjustVertLine) { for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); xx = xx0; diff --git a/splash/SplashClip.h b/splash/SplashClip.h index 3eb2d78..483601a 100644 --- a/splash/SplashClip.h +++ b/splash/SplashClip.h @@ -116,7 +116,8 @@ public: // Clips an anti-aliased line by setting pixels to zero. On entry, // all non-zero pixels are between and . This function // will update and . - void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, + GBool adjustVertLine = gFalse); // Get the rectangle part of the clip region. SplashCoord getXMin() { return xMin; } diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h index 531b945..5d09b82 100644 --- a/splash/SplashTypes.h +++ b/splash/SplashTypes.h @@ -76,6 +76,12 @@ enum SplashColorMode { #endif }; +enum SplashThinLineMode { + splashThinLineDefault, // if SA on: draw solid if requested line width, transformed into + // device space, is less than half a pixel and a shaped line else + splashThinLineSolid, // draw line solid at least with 1 pixel + splashThinLineShape // draw line shaped at least with 1 pixel +}; // number of components in each color mode // (defined in SplashState.cc) extern int splashColorModeNComps[]; diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index b82d355..37e08d5 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -66,7 +66,8 @@ inline void SplashXPath::transform(SplashCoord *matrix, //------------------------------------------------------------------------ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool closeSubpaths) { + SplashCoord flatness, GBool closeSubpaths, + GBool adjustLines, int linePosI) { SplashPathHint *hint; SplashXPathPoint *pts; SplashXPathAdjust *adjusts, *adjust; @@ -127,7 +128,15 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, x0 = splashRound(adj0); x1 = splashRound(adj1); if (x1 == x0) { - x1 = x1 + 1; + if (adjustLines) { + // the adjustment moves thin lines (clip rectangle with + // empty width or height) out of clip area, here we need + // a special adjustment: + x0 = linePosI; + x1 = x0 + 1; + } else { + x1 = x1 + 1; + } } adjusts[i].x0 = (SplashCoord)x0; adjusts[i].x1 = (SplashCoord)x1 - 0.01; diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index db06978..ec082f6 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -50,7 +50,8 @@ public: // space, via . If is true, closes all open // subpaths. SplashXPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool closeSubpaths); + SplashCoord flatness, GBool closeSubpaths, + GBool adjustLines = gFalse, int linePosI = 0); // Copy an expanded path. SplashXPath *copy() { return new SplashXPath(this); } diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index 738cef7..d201da8 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -367,7 +367,7 @@ void SplashXPathScanner::addIntersection(double segYMin, double segYMax, } void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, - int *x0, int *x1, int y) { + int *x0, int *x1, int y, GBool adjustVertLine) { int xx0, xx1, xx, xxMin, xxMax, yy, interEnd; Guchar mask; SplashColorPtr p; @@ -418,8 +418,8 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, xx = xx0; p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); if (xx & 7) { - mask = 0xff >> (xx & 7); - if ((xx & ~7) == (xx1 & ~7)) { + mask = adjustVertLine ? 0xff : 0xff >> (xx & 7); + if (!adjustVertLine && (xx & ~7) == (xx1 & ~7)) { mask &= (Guchar)(0xff00 >> (xx1 & 7)); } *p++ |= mask; @@ -429,7 +429,7 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, *p++ |= 0xff; } if (xx < xx1) { - *p |= (Guchar)(0xff00 >> (xx1 & 7)); + *p |= adjustVertLine ? 0xff : (Guchar)(0xff00 >> (xx1 & 7)); } } if (xx0 < xxMin) { diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 719fae4..211a67d 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -60,7 +60,8 @@ public: // Renders one anti-aliased line into . Returns the min and // max x coordinates with non-zero pixels in and . - void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, + GBool adjustVertLine = gFalse); // Clips an anti-aliased line by setting pixels to zero. On entry, // all non-zero pixels are between and . This function