//======================================================================== // // SplashClip.cc // //======================================================================== //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2010 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2019 Stefan BrĂ¼ns // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #include #include #include #include "goo/gmem.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashBitmap.h" #include "SplashClip.h" //------------------------------------------------------------------------ // SplashClip.flags //------------------------------------------------------------------------ #define splashClipEO 0x01 // use even-odd rule //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, bool antialiasA) { antialias = antialiasA; if (x0 < x1) { xMin = x0; xMax = x1; } else { xMin = x1; xMax = x0; } if (y0 < y1) { yMin = y0; yMax = y1; } else { yMin = y1; yMax = y0; } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; paths = nullptr; flags = nullptr; scanners = nullptr; length = size = 0; } SplashClip::SplashClip(SplashClip *clip) { int yMinAA, yMaxAA; int i; antialias = clip->antialias; xMin = clip->xMin; yMin = clip->yMin; xMax = clip->xMax; yMax = clip->yMax; xMinI = clip->xMinI; yMinI = clip->yMinI; xMaxI = clip->xMaxI; yMaxI = clip->yMaxI; length = clip->length; size = clip->size; paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *)); flags = (unsigned char *)gmallocn(size, sizeof(unsigned char)); scanners = (SplashXPathScanner **) gmallocn(size, sizeof(SplashXPathScanner *)); for (i = 0; i < length; ++i) { paths[i] = clip->paths[i]->copy(); flags[i] = clip->flags[i]; if (antialias) { yMinAA = yMinI * splashAASize; yMaxAA = (yMaxI + 1) * splashAASize - 1; } else { yMinAA = yMinI; yMaxAA = yMaxI; } scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO, yMinAA, yMaxAA); } } SplashClip::~SplashClip() { int i; for (i = 0; i < length; ++i) { delete paths[i]; delete scanners[i]; } gfree(paths); gfree(flags); gfree(scanners); } void SplashClip::grow(int nPaths) { if (length + nPaths > size) { if (size == 0) { size = 32; } while (size < length + nPaths) { size *= 2; } paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *)); flags = (unsigned char *)greallocn(flags, size, sizeof(unsigned char)); scanners = (SplashXPathScanner **) greallocn(scanners, size, sizeof(SplashXPathScanner *)); } } void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { int i; for (i = 0; i < length; ++i) { delete paths[i]; delete scanners[i]; } gfree(paths); gfree(flags); gfree(scanners); paths = nullptr; flags = nullptr; scanners = nullptr; length = size = 0; if (x0 < x1) { xMin = x0; xMax = x1; } else { xMin = x1; xMax = x0; } if (y0 < y1) { yMin = y0; yMax = y1; } else { yMin = y1; yMax = y0; } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { if (x0 < x1) { if (x0 > xMin) { xMin = x0; xMinI = splashFloor(xMin); } if (x1 < xMax) { xMax = x1; xMaxI = splashCeil(xMax) - 1; } } else { if (x1 > xMin) { xMin = x1; xMinI = splashFloor(xMin); } if (x0 < xMax) { xMax = x0; xMaxI = splashCeil(xMax) - 1; } } if (y0 < y1) { if (y0 > yMin) { yMin = y0; yMinI = splashFloor(yMin); } if (y1 < yMax) { yMax = y1; yMaxI = splashCeil(yMax) - 1; } } else { if (y1 > yMin) { yMin = y1; yMinI = splashFloor(yMin); } if (y0 < yMax) { yMax = y0; yMaxI = splashCeil(yMax) - 1; } } return splashOk; } SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool eo) { SplashXPath *xPath; int yMinAA, yMaxAA; xPath = new SplashXPath(path, matrix, flatness, true); // check for an empty path if (xPath->length == 0) { xMax = xMin - 1; yMax = yMin - 1; xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; delete xPath; // check for a rectangle } else if (xPath->length == 4 && ((xPath->segs[0].x0 == xPath->segs[0].x1 && xPath->segs[0].x0 == xPath->segs[1].x0 && xPath->segs[0].x0 == xPath->segs[3].x1 && xPath->segs[2].x0 == xPath->segs[2].x1 && xPath->segs[2].x0 == xPath->segs[1].x1 && xPath->segs[2].x0 == xPath->segs[3].x0 && xPath->segs[1].y0 == xPath->segs[1].y1 && xPath->segs[1].y0 == xPath->segs[0].y1 && xPath->segs[1].y0 == xPath->segs[2].y0 && xPath->segs[3].y0 == xPath->segs[3].y1 && xPath->segs[3].y0 == xPath->segs[0].y0 && xPath->segs[3].y0 == xPath->segs[2].y1) || (xPath->segs[0].y0 == xPath->segs[0].y1 && xPath->segs[0].y0 == xPath->segs[1].y0 && xPath->segs[0].y0 == xPath->segs[3].y1 && xPath->segs[2].y0 == xPath->segs[2].y1 && xPath->segs[2].y0 == xPath->segs[1].y1 && xPath->segs[2].y0 == xPath->segs[3].y0 && xPath->segs[1].x0 == xPath->segs[1].x1 && xPath->segs[1].x0 == xPath->segs[0].x1 && xPath->segs[1].x0 == xPath->segs[2].x0 && xPath->segs[3].x0 == xPath->segs[3].x1 && xPath->segs[3].x0 == xPath->segs[0].x0 && xPath->segs[3].x0 == xPath->segs[2].x1))) { clipToRect(xPath->segs[0].x0, xPath->segs[0].y0, xPath->segs[2].x0, xPath->segs[2].y0); delete xPath; } else { grow(1); if (antialias) { xPath->aaScale(); } xPath->sort(); paths[length] = xPath; flags[length] = eo ? splashClipEO : 0; if (antialias) { yMinAA = yMinI * splashAASize; yMaxAA = (yMaxI + 1) * splashAASize - 1; } else { yMinAA = yMinI; yMaxAA = yMaxI; } scanners[length] = new SplashXPathScanner(xPath, eo, yMinAA, yMaxAA); ++length; } return splashOk; } SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax) { // This tests the rectangle: // x = [rectXMin, rectXMax + 1) (note: rect coords are ints) // y = [rectYMin, rectYMax + 1) // against the clipping region: // x = [xMin, xMax) (note: clipping coords are fp) // y = [yMin, yMax) if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax || (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) { return splashClipAllOutside; } if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax && length == 0) { return splashClipAllInside; } return splashClipPartial; } SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { int i; // This tests the rectangle: // x = [spanXMin, spanXMax + 1) (note: span coords are ints) // y = [spanY, spanY + 1) // against the clipping region: // x = [xMin, xMax) (note: clipping coords are fp) // y = [yMin, yMax) if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin >= xMax || (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY >= yMax) { return splashClipAllOutside; } if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) { return splashClipPartial; } if (antialias) { for (i = 0; i < length; ++i) { if (!scanners[i]->testSpan(spanXMin * splashAASize, spanXMax * splashAASize + (splashAASize - 1), spanY * splashAASize)) { return splashClipPartial; } } } else { for (i = 0; i < length; ++i) { if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) { return splashClipPartial; } } } return splashClipAllInside; } void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, bool adjustVertLine) { int xx0, xx1, xx, yy, i; SplashColorPtr p; // zero out pixels with x < xMin xx0 = *x0 * splashAASize; xx1 = splashFloor(xMin * splashAASize); if (xx1 > aaBuf->getWidth()) { xx1 = aaBuf->getWidth(); } if (xx0 < xx1) { xx0 &= ~7; for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); for (xx = xx0; xx + 7 < xx1; xx += 8) { *p++ = 0; } if (xx < xx1 && !adjustVertLine) { *p &= 0xff >> (xx1 & 7); } } *x0 = splashFloor(xMin); } // zero out pixels with x > xMax xx0 = splashFloor(xMax * splashAASize) + 1; if (xx0 < 0) { xx0 = 0; } xx1 = (*x1 + 1) * splashAASize; if (xx0 < xx1 && !adjustVertLine) { for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); xx = xx0; if (xx & 7) { *p &= 0xff00 >> (xx & 7); xx = (xx & ~7) + 8; ++p; } for (; xx < xx1; xx += 8) { *p++ = 0; } } *x1 = splashFloor(xMax); } // check the paths for (i = 0; i < length; ++i) { scanners[i]->clipAALine(aaBuf, x0, x1, y); } if (*x0 > *x1) { *x0 = *x1; } if (*x0 < 0) { *x0 = 0; } if ((*x0>>1) >= aaBuf->getRowSize()) { xx0 = *x0; *x0 = (aaBuf->getRowSize() - 1) << 1; if (xx0 & 1) { *x0 = *x0 + 1; } } if (*x1 < *x0) { *x1 = *x0; } if ((*x1>>1) >= aaBuf->getRowSize()) { xx0 = *x1; *x1 = (aaBuf->getRowSize() - 1) << 1; if (xx0 & 1) { *x1 = *x1 + 1; } } } bool SplashClip::testClipPaths(int x, int y) { if (antialias) { x *= splashAASize; y *= splashAASize; } for (int i = 0; i < length; ++i) { if (!scanners[i]->test(x, y)) { return false; } } return true; }