diff options
Diffstat (limited to 'WebKit')
| -rw-r--r-- | WebKit/android/nav/SelectText.cpp | 363 |
1 files changed, 246 insertions, 117 deletions
diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp index 9df6ef5..030141d 100644 --- a/WebKit/android/nav/SelectText.cpp +++ b/WebKit/android/nav/SelectText.cpp @@ -173,6 +173,8 @@ public: }; #define HYPHEN_MINUS 0x2D // ASCII hyphen +#define SOLIDUS 0x2F // ASCII slash +#define REVERSE_SOLIDUS 0x5C // ASCII backslash #define HYPHEN 0x2010 // unicode hyphen, first in range of dashes #define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes #define TOUCH_SLOP 10 // additional distance from character rect when hit @@ -229,7 +231,8 @@ public: { bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY; if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR) - || mLastUni == HYPHEN_MINUS) && newBaseLine) + || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS + || mLastUni == REVERSE_SOLIDUS) && newBaseLine) { return false; } @@ -283,7 +286,7 @@ public: SkFixedToScalar(mLastGlyph.fLSB.fY), *mPaint); const SkBounder::GlyphRec& g1 = spaceChecker.mBounder.mFirstGlyph; const SkBounder::GlyphRec& g2 = spaceChecker.mBounder.mLastGlyph; - DBG_NAV_LOGD("g1=(%g, %g,%g, %g) g2=(%g, %g, %g, %g)", + DBG_NAV_LOGD("g1=(%g, %g, %g, %g) g2=(%g, %g, %g, %g)", SkFixedToScalar(g1.fLSB.fX), SkFixedToScalar(g1.fLSB.fY), SkFixedToScalar(g1.fRSB.fX), SkFixedToScalar(g1.fRSB.fY), SkFixedToScalar(g2.fLSB.fX), SkFixedToScalar(g2.fLSB.fY), @@ -298,10 +301,10 @@ public: SkFixedToScalar(gap), SkFixedToScalar(overlap), SkFixedToScalar(gapOne), SkFixedToScalar(gapTwo), SkFixedToScalar(minSpaceWidth())); - // FIXME: the -1/2 below takes care of slop beween the computed gap + // FIXME: the -1/8 below takes care of slop beween the computed gap // and the actual space width -- it's a rounding error from // moving from fixed to float and back and could be much smaller. - return gap >= minSpaceWidth() - SK_Fixed1 / 2; + return gap >= minSpaceWidth() - SK_Fixed1 / 8; } SkFixed minSpaceWidth() @@ -588,33 +591,195 @@ static bool baseLinesAgree(const SkIRect& rectA, int baseA, || (rectB.fTop < baseA && rectB.fBottom >= baseA); } -class MultilineBuilder : public CommonCheck { -public: - MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end, - int endBase, const SkIRect& area, SkRegion* region) - : INHERITED(area.width(),area.height()) +class BuilderCheck : public CommonCheck { +protected: + enum IntersectionType { + NO_INTERSECTION, // debugging printf expects this to equal zero + LAST_INTERSECTION, // debugging printf expects this to equal one + WAIT_FOR_INTERSECTION + }; + + BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end, + int endBase, const SkIRect& area) + : INHERITED(area.width(), area.height()) , mCapture(false) , mEnd(end) , mEndBase(endBase) - , mFlipped(false) - , mSelectRegion(region) , mStart(start) , mStartBase(startBase) { mEnd.offset(-area.fLeft, -area.fTop); mEndBase -= area.fTop; + mEndExtra.setEmpty(); mLast.setEmpty(); mLastBase = INT_MAX; + mSelectRect.setEmpty(); mStart.offset(-area.fLeft, -area.fTop); mStartBase -= area.fTop; + mStartExtra.setEmpty(); + DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d" + " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d", + mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase, + mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase); + } + + int checkFlipRect(const SkIRect& full, int fullBase) { + mCollectFull = false; + // is the text to collect between the selection top and bottom? + if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) { + if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d", + mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase); + return mLastIntersects; + } + // is the text to the left of the selection start? + if (baseLinesAgree(mStart, mStartBase, full, fullBase) + && full.fLeft < mStart.fLeft) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" + " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d", + mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, + mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase); + mStartExtra.join(full); + return mLastIntersects; + } + // is the text to the right of the selection end? + if (baseLinesAgree(mEnd, mEndBase, full, fullBase) + && full.fRight > mEnd.fRight) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" + " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d", + mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, + mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase); + mEndExtra.join(full); + return mLastIntersects; + } + int spaceGap = SkFixedRound(minSpaceWidth() * 3); + // should text to the left of the start be added to the selection bounds? + if (!mStartExtra.isEmpty()) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)" + " mStartExtra=(%d,%d,r=%d,b=%d)", + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, + mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom); + if (mStartExtra.fRight + spaceGap >= mStart.fLeft) + mSelectRect.join(mStartExtra); + mStartExtra.setEmpty(); + } + // should text to the right of the end be added to the selection bounds? + if (!mEndExtra.isEmpty()) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)" + " mEndExtra=(%d,%d,r=%d,b=%d)", + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, + mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom); + if (mEndExtra.fLeft - spaceGap <= mEnd.fRight) + mSelectRect.join(mEndExtra); + mEndExtra.setEmpty(); + } + bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase); + bool adjacent = (full.fLeft - mLast.fRight) < spaceGap; + // is this the first, or are there more characters on the same line? + if (mLast.isEmpty() || (sameBaseLine && adjacent)) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" + " mSelectRect=(%d,%d,r=%d,b=%d)", + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); + mLast.join(full); + mLastIntersects = SkIRect::Intersects(mLast, mSelectRect); + return WAIT_FOR_INTERSECTION; + } + if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4" + " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" + " full=(%d,%d,r=%d,b=%d) fullBase=%d" + " mSelectRect=(%d,%d,r=%d,b=%d)" + " mStartExtra=(%d,%d,r=%d,b=%d)" + " mEndExtra=(%d,%d,r=%d,b=%d)", + mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, + full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, + mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom, + mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom); + // after the caller determines what to do with the last collection, + // start the collection over with full and fullBase. + mCollectFull = true; + return mLastIntersects; + } + + bool resetLast(const SkIRect& full, int fullBase) + { + if (mCollectFull) { + mLast = full; + mLastBase = fullBase; + mLastIntersects = SkIRect::Intersects(mLast, mSelectRect); + } else { + mLast.setEmpty(); + mLastBase = INT_MAX; + mLastIntersects = false; + } + return mCollectFull; + } + + void setFlippedState() + { + mSelectRect = mStart; + mSelectRect.join(mEnd); + DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)", + mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); + mLast.setEmpty(); + mLastBase = INT_MAX; + mLastIntersects = NO_INTERSECTION; + } + + bool mCapture; + bool mCollectFull; + SkIRect mEnd; + int mEndBase; + SkIRect mEndExtra; + bool mFlipped; + SkIRect mLast; + int mLastBase; + int mLastIntersects; + SkIRect mSelectRect; + SkIRect mStart; + SkIRect mStartExtra; + int mStartBase; +private: + typedef CommonCheck INHERITED; + +}; + +class MultilineBuilder : public BuilderCheck { +public: + MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end, + int endBase, const SkIRect& area, SkRegion* region) + : INHERITED(start, startBase, end, endBase, area) + , mSelectRegion(region) + { + mFlipped = false; + } + + void addLastToRegion() { + if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)", + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); + mSelectRegion->op(mLast, SkRegion::kUnion_Op); } void finish() { - if (!mFlipped || SkIRect::Intersects(mLast, mSelectRect)) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); - mSelectRegion->op(mLast, SkRegion::kUnion_Op); - } + if (!mFlipped || !mLastIntersects) + return; + addLastToRegion(); } // return true if capture end was not found after capture begin @@ -623,10 +788,7 @@ public: if (!mCapture) return false; mFlipped = true; - mSelectRect = mStart; - mSelectRect.join(mEnd); - mLast.setEmpty(); - mLastBase = INT_MAX; + setFlippedState(); mSelectRegion->setEmpty(); return true; } @@ -636,54 +798,42 @@ public: full.set(rect.fLeft, top(), rect.fRight, bottom()); int fullBase = base(); if (mFlipped) { - if (fullBase < mStart.fTop || fullBase > mEnd.fBottom - || (baseLinesAgree(mStart, mStartBase, full, fullBase) - && full.fLeft < mStart.fLeft) - || (baseLinesAgree(mEnd, mEndBase, full, fullBase) - && full.fRight > mEnd.fRight)) { - return false; - } - if (baseLinesAgree(mLast, mLastBase, full, fullBase) - && (full.fLeft - mLast.fRight < minSpaceWidth() * 3 - || (SkIRect::Intersects(full, mSelectRect) - && SkIRect::Intersects(mLast, mSelectRect)))) { - mLast.join(full); - return false; - } - finish(); - mLast = full; - mLastBase = fullBase; + int intersectType = checkFlipRect(full, fullBase); + if (intersectType == LAST_INTERSECTION) + addLastToRegion(); + if (intersectType != WAIT_FOR_INTERSECTION) + resetLast(full, fullBase); return false; } - if (full == mStart) + if (full == mStart) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)", + full.fLeft, full.fTop, full.fRight, full.fBottom); mCapture = true; + } if (mCapture) { - if (baseLinesAgree(mLast, mLastBase, full, fullBase)) + bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase); + if (sameLines) mLast.join(full); - else { - finish(); + if (!sameLines || full == mEnd) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)", + mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); + addLastToRegion(); mLast = full; mLastBase = fullBase; } } - if (full == mEnd) + if (full == mEnd) { + if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)", + full.fLeft, full.fTop, full.fRight, full.fBottom); mCapture = false; + } return false; } protected: - bool mCapture; - SkIRect mEnd; - int mEndBase; - bool mFlipped; - SkIRect mLast; - int mLastBase; - SkIRect mSelectRect; SkRegion* mSelectRegion; - SkIRect mStart; - int mStartBase; private: - typedef CommonCheck INHERITED; + typedef BuilderCheck INHERITED; }; static inline bool compareBounds(const SkIRect* first, const SkIRect* second) @@ -691,33 +841,23 @@ static inline bool compareBounds(const SkIRect* first, const SkIRect* second) return first->fTop < second->fTop; } -class TextExtractor : public CommonCheck { +class TextExtractor : public BuilderCheck { public: TextExtractor(const SkIRect& start, int startBase, const SkIRect& end, int endBase, const SkIRect& area, bool flipped) - : INHERITED(area.width(), area.height()) - , mEnd(end) - , mEndBase(endBase) - , mFlipped(flipped) - , mRecord(false) - , mSelectRect(start) + : INHERITED(start, startBase, end, endBase, area) + , mSelectStartIndex(-1) , mSkipFirstSpace(true) // don't start with a space - , mStart(start) - , mStartBase(startBase) { - mEmpty.setEmpty(); - mEnd.offset(-area.fLeft, -area.fTop); - mEndBase -= area.fTop; - mLast.setEmpty(); - mLastBase = INT_MAX; - mSelectRect.join(end); - mSelectRect.offset(-area.fLeft, -area.fTop); - mStart.offset(-area.fLeft, -area.fTop); - mStartBase -= area.fTop; + mFlipped = flipped; + if (flipped) + setFlippedState(); } void addCharacter(const SkBounder::GlyphRec& rec) { + if (mSelectStartIndex < 0) + mSelectStartIndex = mSelectText.count(); if (!mSkipFirstSpace) { if (addNewLine(rec)) { DBG_NAV_LOG("write new line"); @@ -742,8 +882,24 @@ public: } } + void addLast() + { + *mSelectBounds.append() = mLast; + *mSelectStart.append() = mSelectStartIndex; + *mSelectEnd.append() = mSelectText.count(); + } + + /* Text characters are collected before it's been determined that the + characters are part of the selection. The bounds describe valid parts + of the selection, but the bounds are out of order. + + This sorts the characters by sorting the bounds, then copying the + characters that were captured. + */ void finish() { + if (mLastIntersects) + addLast(); Vector<SkIRect*> sortedBounds; SkTDArray<uint16_t> temp; int index; @@ -754,11 +910,9 @@ public: std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds); int lastEnd = -1; for (index = 0; index < mSelectBounds.count(); index++) { - if (sortedBounds[index]->isEmpty()) - continue; int order = sortedBounds[index] - &mSelectBounds[0]; - int start = order > 0 ? mSelectCount[order - 1] : 0; - int end = mSelectCount[order]; + int start = mSelectStart[order]; + int end = mSelectEnd[order]; DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end, mSelectBounds[order].fTop); int count = temp.count(); @@ -781,46 +935,28 @@ public: full.set(rect.fLeft, top(), rect.fRight, bottom()); int fullBase = base(); if (mFlipped) { - if (fullBase < mStart.fTop || fullBase > mEnd.fBottom - || (baseLinesAgree(mStart, mStartBase, full, fullBase) - && full.fLeft < mStart.fLeft) - || (baseLinesAgree(mEnd, mEndBase, full, fullBase) - && full.fRight > mEnd.fRight)) { - mSkipFirstSpace = true; - return false; - } - if (baseLinesAgree(mLast, mLastBase, full, fullBase) - && (full.fLeft - mLast.fRight < minSpaceWidth() * 3 - || (SkIRect::Intersects(full, mSelectRect) - && SkIRect::Intersects(mLast, mSelectRect)))) { - mLast.join(full); - addCharacter(rec); - return false; + int intersectType = checkFlipRect(full, fullBase); + if (WAIT_FOR_INTERSECTION == intersectType) + addCharacter(rec); // may not be copied + else { + if (LAST_INTERSECTION == intersectType) + addLast(); + else + mSkipFirstSpace = true; + mSelectStartIndex = -1; + if (resetLast(full, fullBase)) + addCharacter(rec); // may not be copied } - if (VERBOSE_LOGGING) DBG_NAV_LOGD("baseLinesAgree=%s" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mSelectRect=(%d,%d,r=%d,b=%d)", - baseLinesAgree(mLast, mLastBase, full, fullBase) ? "true" : "false", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); - *mSelectBounds.append() = SkIRect::Intersects(mLast, mSelectRect) - ? mLast : mEmpty; - *mSelectCount.append() = mSelectText.count(); - addCharacter(rec); - mLast = full; - mLastBase = fullBase; return false; } if (full == mStart) - mRecord = true; - if (mRecord) + mCapture = true; + if (mCapture) addCharacter(rec); else mSkipFirstSpace = true; if (full == mEnd) - mRecord = false; + mCapture = false; return false; } @@ -845,21 +981,14 @@ public: protected: SkIRect mEmpty; - SkIRect mEnd; - int mEndBase; - bool mFlipped; - SkIRect mLast; - int mLastBase; - bool mRecord; SkTDArray<SkIRect> mSelectBounds; - SkTDArray<int> mSelectCount; - SkIRect mSelectRect; + SkTDArray<int> mSelectEnd; + SkTDArray<int> mSelectStart; + int mSelectStartIndex; SkTDArray<uint16_t> mSelectText; bool mSkipFirstSpace; - SkIRect mStart; - int mStartBase; private: - typedef CommonCheck INHERITED; + typedef BuilderCheck INHERITED; }; class TextCanvas : public SkCanvas { |
