Home | History | Annotate | Download | only in nav
      1 /*
      2  * Copyright 2008, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #define LOG_TAG "webviewglue"
     27 
     28 #include "CachedPrefix.h"
     29 #include "BidiResolver.h"
     30 #include "CachedRoot.h"
     31 #include "LayerAndroid.h"
     32 #include "SelectText.h"
     33 #include "SkBitmap.h"
     34 #include "SkBounder.h"
     35 #include "SkCanvas.h"
     36 #include "SkGradientShader.h"
     37 #include "SkMatrix.h"
     38 #include "SkPicture.h"
     39 #include "SkPixelXorXfermode.h"
     40 #include "SkPoint.h"
     41 #include "SkRect.h"
     42 #include "SkRegion.h"
     43 #include "SkUtils.h"
     44 #include "TextRun.h"
     45 
     46 #ifdef DEBUG_NAV_UI
     47 #include "CString.h"
     48 #endif
     49 
     50 #define VERBOSE_LOGGING 0
     51 // #define EXTRA_NOISY_LOGGING 1
     52 
     53 // TextRunIterator has been copied verbatim from GraphicsContext.cpp
     54 namespace WebCore {
     55 
     56 class TextRunIterator {
     57 public:
     58     TextRunIterator()
     59         : m_textRun(0)
     60         , m_offset(0)
     61     {
     62     }
     63 
     64     TextRunIterator(const TextRun* textRun, unsigned offset)
     65         : m_textRun(textRun)
     66         , m_offset(offset)
     67     {
     68     }
     69 
     70     TextRunIterator(const TextRunIterator& other)
     71         : m_textRun(other.m_textRun)
     72         , m_offset(other.m_offset)
     73     {
     74     }
     75 
     76     unsigned offset() const { return m_offset; }
     77     void increment() { m_offset++; }
     78     bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
     79     UChar current() const { return (*m_textRun)[m_offset]; }
     80     WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
     81 
     82     bool operator==(const TextRunIterator& other)
     83     {
     84         return m_offset == other.m_offset && m_textRun == other.m_textRun;
     85     }
     86 
     87     bool operator!=(const TextRunIterator& other) { return !operator==(other); }
     88 
     89 private:
     90     const TextRun* m_textRun;
     91     int m_offset;
     92 };
     93 
     94 // ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
     95 void ReverseBidi(UChar* chars, int len) {
     96     using namespace WTF::Unicode;
     97     WTF::Vector<UChar> result;
     98     result.reserveCapacity(len);
     99     TextRun run(chars, len);
    100     BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
    101     bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
    102         BidiContext::create(0, LeftToRight, false)));
    103     bidiResolver.setPosition(TextRunIterator(&run, 0));
    104     bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
    105     if (!bidiResolver.runCount())
    106         return;
    107     BidiCharacterRun* bidiRun = bidiResolver.firstRun();
    108     while (bidiRun) {
    109         int bidiStart = bidiRun->start();
    110         int bidiStop = bidiRun->stop();
    111         int size = result.size();
    112         int bidiCount = bidiStop - bidiStart;
    113         result.append(chars + bidiStart, bidiCount);
    114         if (bidiRun->level() % 2) {
    115             UChar* start = &result[size];
    116             UChar* end = start + bidiCount;
    117             // reverse the order of any RTL substrings
    118             while (start < end) {
    119                 UChar temp = *start;
    120                 *start++ = *--end;
    121                 *end = temp;
    122             }
    123             start = &result[size];
    124             end = start + bidiCount - 1;
    125             // if the RTL substring had a surrogate pair, restore its order
    126             while (start < end) {
    127                 UChar trail = *start++;
    128                 if (!U16_IS_SURROGATE(trail))
    129                     continue;
    130                 start[-1] = *start; // lead
    131                 *start++ = trail;
    132             }
    133         }
    134         bidiRun = bidiRun->next();
    135     }
    136     bidiResolver.deleteRuns();
    137     memcpy(chars, &result[0], len * sizeof(UChar));
    138 }
    139 
    140 }
    141 
    142 namespace android {
    143 
    144 /* SpaceBounds and SpaceCanvas are used to measure the left and right side
    145  * bearings of two consecutive glyphs to help determine if the glyphs were
    146  * originally laid out with a space character between the glyphs.
    147  */
    148 class SpaceBounds : public SkBounder {
    149 public:
    150     virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& rec)
    151     {
    152         mFirstGlyph = mLastGlyph;
    153         mLastGlyph = rec;
    154         return false;
    155     }
    156 
    157     SkBounder::GlyphRec mFirstGlyph;
    158     SkBounder::GlyphRec mLastGlyph;
    159 };
    160 
    161 class SpaceCanvas : public SkCanvas {
    162 public:
    163     SpaceCanvas(const SkIRect& area)
    164     {
    165         setBounder(&mBounder);
    166         SkBitmap bitmap;
    167         bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
    168             area.height());
    169         setBitmapDevice(bitmap);
    170         translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
    171     }
    172 
    173     SpaceBounds mBounder;
    174 };
    175 
    176 #define HYPHEN_MINUS 0x2D // ASCII hyphen
    177 #define SOLIDUS 0x2F // ASCII slash
    178 #define REVERSE_SOLIDUS 0x5C // ASCII backslash
    179 #define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
    180 #define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
    181 #define TOUCH_SLOP 10 // additional distance from character rect when hit
    182 
    183 class CommonCheck : public SkBounder {
    184 public:
    185     CommonCheck(int width, int height)
    186         : mHeight(height)
    187         , mLastUni(0)
    188         , mMatrix(0)
    189         , mPaint(0)
    190         , mWidth(width)
    191     {
    192         mLastGlyph.fGlyphID = static_cast<uint16_t>(-1);
    193         reset();
    194     }
    195 
    196     int base() {
    197         if (mBase == INT_MAX) {
    198             SkPoint result;
    199             mMatrix->mapXY(0, mY, &result);
    200             mBase = SkScalarFloor(result.fY);
    201         }
    202         return mBase;
    203     }
    204 
    205      int bottom() {
    206         if (mBottom == INT_MAX) {
    207             SkPoint result;
    208             SkPaint::FontMetrics metrics;
    209             mPaint->getFontMetrics(&metrics);
    210             mMatrix->mapXY(0, metrics.fDescent + mY, &result);
    211             mBottom = SkScalarCeil(result.fY);
    212         }
    213         return mBottom;
    214     }
    215 
    216 #if DEBUG_NAV_UI
    217     // make current (possibily uncomputed) value visible for debugging
    218     int bottomDebug() const
    219     {
    220         return mBottom;
    221     }
    222 #endif
    223 
    224     bool addNewLine(const SkBounder::GlyphRec& rec)
    225     {
    226         SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY);
    227         SkFixed lineHeight = SkIntToFixed(bottom() - top());
    228         return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5
    229     }
    230 
    231     bool addSpace(const SkBounder::GlyphRec& rec)
    232     {
    233         bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
    234         if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR)
    235             || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS
    236             || mLastUni == REVERSE_SOLIDUS) && newBaseLine)
    237         {
    238             return false;
    239         }
    240         return isSpace(rec);
    241     }
    242 
    243     void finishGlyph()
    244     {
    245         mLastGlyph = mLastCandidate;
    246         mLastUni = mLastUniCandidate;
    247     }
    248 
    249     SkUnichar getUniChar(const SkBounder::GlyphRec& rec)
    250     {
    251         SkUnichar unichar;
    252         SkPaint utfPaint = *mPaint;
    253         utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    254         utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
    255         return unichar;
    256     }
    257 
    258     bool isSpace(const SkBounder::GlyphRec& rec)
    259     {
    260         DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)"
    261             " rec=((%g, %g),(%g, %g), %d)"
    262             " mMinSpaceWidth=%g mLastUni=0x%04x '%c'",
    263             SkFixedToScalar(mLastGlyph.fLSB.fX),
    264             SkFixedToScalar(mLastGlyph.fLSB.fY),
    265             SkFixedToScalar(mLastGlyph.fRSB.fX),
    266             SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
    267             SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY),
    268             SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY),
    269             rec.fGlyphID,
    270             SkFixedToScalar(mMinSpaceWidth),
    271             mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
    272         bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
    273         if (newBaseLine)
    274             return true;
    275         SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX;
    276         SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX;
    277         if (gapOne < 0 && gapTwo < 0)
    278             return false; // overlaps
    279         uint16_t test[2];
    280         test[0] = mLastGlyph.fGlyphID;
    281         test[1] = rec.fGlyphID;
    282         SkIRect area;
    283         area.set(0, 0, mWidth, mHeight);
    284         SpaceCanvas spaceChecker(area);
    285         spaceChecker.drawText(test, sizeof(test),
    286             SkFixedToScalar(mLastGlyph.fLSB.fX),
    287             SkFixedToScalar(mLastGlyph.fLSB.fY), *mPaint);
    288         const SkBounder::GlyphRec& g1 = spaceChecker.mBounder.mFirstGlyph;
    289         const SkBounder::GlyphRec& g2 = spaceChecker.mBounder.mLastGlyph;
    290         DBG_NAV_LOGD("g1=(%g, %g, %g, %g) g2=(%g, %g, %g, %g)",
    291             SkFixedToScalar(g1.fLSB.fX), SkFixedToScalar(g1.fLSB.fY),
    292             SkFixedToScalar(g1.fRSB.fX), SkFixedToScalar(g1.fRSB.fY),
    293             SkFixedToScalar(g2.fLSB.fX), SkFixedToScalar(g2.fLSB.fY),
    294             SkFixedToScalar(g2.fRSB.fX), SkFixedToScalar(g2.fRSB.fY));
    295         gapOne = SkFixedAbs(gapOne);
    296         gapTwo = SkFixedAbs(gapTwo);
    297         SkFixed gap = gapOne < gapTwo ? gapOne : gapTwo;
    298         SkFixed overlap = g2.fLSB.fX - g1.fRSB.fX;
    299         if (overlap < 0)
    300             gap -= overlap;
    301         DBG_NAV_LOGD("gap=%g overlap=%g gapOne=%g gapTwo=%g minSpaceWidth()=%g",
    302             SkFixedToScalar(gap), SkFixedToScalar(overlap),
    303             SkFixedToScalar(gapOne), SkFixedToScalar(gapTwo),
    304             SkFixedToScalar(minSpaceWidth()));
    305         // FIXME: the -1/8 below takes care of slop beween the computed gap
    306         // and the actual space width -- it's a rounding error from
    307         // moving from fixed to float and back and could be much smaller.
    308         return gap >= minSpaceWidth() - SK_Fixed1 / 8;
    309     }
    310 
    311     SkFixed minSpaceWidth()
    312     {
    313         if (mMinSpaceWidth == SK_FixedMax) {
    314             SkPaint charPaint = *mPaint;
    315             charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
    316             SkScalar width = charPaint.measureText(" ", 1);
    317             mMinSpaceWidth = SkScalarToFixed(width * mMatrix->getScaleX());
    318             DBG_NAV_LOGD("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)"
    319                 " mMinSpaceWidth=%g", width,
    320                 mMatrix->getScaleX(), mMatrix->getScaleY(),
    321                 mMatrix->getTranslateX(), mMatrix->getTranslateY(),
    322                 SkFixedToScalar(mMinSpaceWidth));
    323         }
    324         return mMinSpaceWidth;
    325     }
    326 
    327     void recordGlyph(const SkBounder::GlyphRec& rec)
    328     {
    329         mLastCandidate = rec;
    330         mLastUniCandidate = getUniChar(rec);
    331     }
    332 
    333     void reset()
    334     {
    335         mMinSpaceWidth = SK_FixedMax; // mark as uninitialized
    336         mBase = mBottom = mTop = INT_MAX; // mark as uninitialized
    337     }
    338 
    339     void set(CommonCheck& check)
    340     {
    341         mLastGlyph = check.mLastGlyph;
    342         mLastUni = check.mLastUni;
    343         mMatrix = check.mMatrix;
    344         mPaint = check.mPaint;
    345         reset();
    346     }
    347 
    348     void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
    349             const void* text)
    350     {
    351         mMatrix = &matrix;
    352         mPaint = &paint;
    353         mText = static_cast<const uint16_t*>(text);
    354         mY = y;
    355         reset();
    356     }
    357 
    358     int top() {
    359         if (mTop == INT_MAX) {
    360             SkPoint result;
    361             SkPaint::FontMetrics metrics;
    362             mPaint->getFontMetrics(&metrics);
    363             mMatrix->mapXY(0, metrics.fAscent + mY, &result);
    364             mTop = SkScalarFloor(result.fY);
    365         }
    366         return mTop;
    367     }
    368 
    369 #if DEBUG_NAV_UI
    370     // make current (possibily uncomputed) value visible for debugging
    371     int topDebug() const
    372     {
    373         return mTop;
    374     }
    375 #endif
    376 
    377 protected:
    378     int mHeight;
    379     SkBounder::GlyphRec mLastCandidate;
    380     SkBounder::GlyphRec mLastGlyph;
    381     SkUnichar mLastUni;
    382     SkUnichar mLastUniCandidate;
    383     const SkMatrix* mMatrix;
    384     const SkPaint* mPaint;
    385     const uint16_t* mText;
    386     int mWidth;
    387     SkScalar mY;
    388 private:
    389     int mBase;
    390     int mBottom;
    391     SkFixed mMinSpaceWidth;
    392     int mTop;
    393     friend class EdgeCheck;
    394 };
    395 
    396 class FirstCheck : public CommonCheck {
    397 public:
    398     FirstCheck(int x, int y, const SkIRect& area)
    399         : INHERITED(area.width(), area.height())
    400         , mFocusX(x - area.fLeft)
    401         , mFocusY(y - area.fTop)
    402         , mRecordGlyph(false)
    403     {
    404         reset();
    405     }
    406 
    407     const SkIRect& adjustedBounds(const SkIRect& area, int* base)
    408     {
    409         *base = mBestBase + area.fTop;
    410         mBestBounds.offset(area.fLeft, area.fTop);
    411         DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
    412             mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
    413             mBestBounds.fBottom, topDebug(), bottomDebug());
    414         return mBestBounds;
    415     }
    416 
    417     virtual bool onIRectGlyph(const SkIRect& rect,
    418         const SkBounder::GlyphRec& rec)
    419     {
    420         /* compute distance from rectangle center.
    421          * centerX = (rect.L + rect.R) / 2
    422          * multiply centerX and comparison x by 2 to retain better precision
    423          */
    424         int dx = rect.fLeft + rect.fRight - (mFocusX << 1);
    425         int dy = top() + bottom() - (mFocusY << 1);
    426         int distance = dx * dx + dy * dy;
    427 #ifdef EXTRA_NOISY_LOGGING
    428         if (distance < 500 || abs(distance - mDistance) < 500)
    429             DBG_NAV_LOGD("FirstCheck distance=%d mDistance=%d", distance, mDistance);
    430 #endif
    431         if (mDistance > distance) {
    432             mBestBase = base();
    433             mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
    434             if (distance < 100) {
    435                 DBG_NAV_LOGD("FirstCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
    436                     mBestBounds.fLeft, mBestBounds.fTop,
    437                     mBestBounds.fRight, mBestBounds.fBottom, distance >> 2);
    438             }
    439             mDistance = distance;
    440             if (mRecordGlyph)
    441                 recordGlyph(rec);
    442         }
    443         return false;
    444     }
    445 
    446     void reset()
    447     {
    448         mBestBounds.setEmpty();
    449         mDistance = INT_MAX;
    450     }
    451 
    452     void setRecordGlyph()
    453     {
    454         mRecordGlyph = true;
    455     }
    456 
    457 protected:
    458     int mBestBase;
    459     SkIRect mBestBounds;
    460     int mDistance;
    461     int mFocusX;
    462     int mFocusY;
    463     bool mRecordGlyph;
    464 private:
    465     typedef CommonCheck INHERITED;
    466 };
    467 
    468 class EdgeCheck : public FirstCheck {
    469 public:
    470     EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left)
    471         : INHERITED(x, y, area)
    472         , mLast(area.width(), area.height())
    473         , mLeft(left)
    474     {
    475         mLast.set(last);
    476         mLastGlyph = last.mLastGlyph;
    477         mLastUni = last.mLastUni;
    478     }
    479 
    480     bool adjacent()
    481     {
    482         return !mLast.isSpace(mLastGlyph);
    483     }
    484 
    485     const SkIRect& bestBounds(int* base)
    486     {
    487         *base = mBestBase;
    488         return mBestBounds;
    489     }
    490 
    491     virtual bool onIRectGlyph(const SkIRect& rect,
    492         const SkBounder::GlyphRec& rec)
    493     {
    494         int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX;
    495         int dy = ((top() + bottom()) >> 1) - mFocusY;
    496         if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) {
    497             if (abs(dx) <= 10 && abs(dy) <= 10) {
    498                 DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d",
    499                     rect.fLeft, rect.fRight, mFocusX, dx, dy);
    500             }
    501             return false;
    502         }
    503         int distance = dx * dx + dy * dy;
    504         if (mDistance > distance) {
    505             if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) {
    506                 DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g",
    507                 SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX));
    508                 return false;
    509             }
    510             recordGlyph(rec);
    511             mDistance = distance;
    512             mBestBase = base();
    513             mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
    514             if (distance <= 100) {
    515                 DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
    516                     mBestBounds.fLeft, mBestBounds.fTop,
    517                     mBestBounds.fRight, mBestBounds.fBottom, distance);
    518             }
    519         }
    520         return false;
    521     }
    522 
    523     void shiftStart(SkIRect bounds)
    524     {
    525         DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d",
    526             mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight);
    527         reset();
    528         mFocusX = mLeft ? bounds.fLeft : bounds.fRight;
    529         mLast.set(*this);
    530     }
    531 
    532 protected:
    533     CommonCheck mLast;
    534     bool mLeft;
    535 private:
    536     typedef FirstCheck INHERITED;
    537 };
    538 
    539 class FindFirst : public CommonCheck {
    540 public:
    541     FindFirst(int width, int height)
    542         : INHERITED(width, height)
    543     {
    544         mBestBounds.set(width, height, width, height);
    545     }
    546 
    547     const SkIRect& bestBounds(int* base)
    548     {
    549         *base = mBestBase;
    550         return mBestBounds;
    551     }
    552 
    553     virtual bool onIRect(const SkIRect& rect)
    554     {
    555         if (mBestBounds.isEmpty()) {
    556             mBestBase = base();
    557             mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
    558         }
    559         return false;
    560     }
    561 
    562 protected:
    563     int mBestBase;
    564     SkIRect mBestBounds;
    565 private:
    566     typedef CommonCheck INHERITED;
    567 };
    568 
    569 class FindLast : public FindFirst {
    570 public:
    571     FindLast(int width, int height)
    572         : INHERITED(width, height)
    573     {
    574         mBestBounds.setEmpty();
    575     }
    576 
    577     virtual bool onIRect(const SkIRect& rect)
    578     {
    579         mBestBase = base();
    580         mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
    581         return false;
    582     }
    583 
    584 private:
    585     typedef FindFirst INHERITED;
    586 };
    587 
    588 static bool baseLinesAgree(const SkIRect& rectA, int baseA,
    589     const SkIRect& rectB, int baseB)
    590 {
    591     return (rectA.fTop < baseB && rectA.fBottom >= baseB)
    592         || (rectB.fTop < baseA && rectB.fBottom >= baseA);
    593 }
    594 
    595 class BuilderCheck : public CommonCheck {
    596 protected:
    597     enum IntersectionType {
    598         NO_INTERSECTION, // debugging printf expects this to equal zero
    599         LAST_INTERSECTION, // debugging printf expects this to equal one
    600         WAIT_FOR_INTERSECTION
    601     };
    602 
    603     BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end,
    604         int endBase, const SkIRect& area)
    605         : INHERITED(area.width(), area.height())
    606         , mCapture(false)
    607         , mEnd(end)
    608         , mEndBase(endBase)
    609         , mStart(start)
    610         , mStartBase(startBase)
    611     {
    612         mEnd.offset(-area.fLeft, -area.fTop);
    613         mEndBase -= area.fTop;
    614         mEndExtra.setEmpty();
    615         mLast.setEmpty();
    616         mLastBase = INT_MAX;
    617         mSelectRect.setEmpty();
    618         mStart.offset(-area.fLeft, -area.fTop);
    619         mStartBase -= area.fTop;
    620         mStartExtra.setEmpty();
    621         DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d"
    622             " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
    623             mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase,
    624             mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
    625     }
    626 
    627     int checkFlipRect(const SkIRect& full, int fullBase) {
    628         mCollectFull = false;
    629         // is the text to collect between the selection top and bottom?
    630         if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) {
    631             if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1"
    632                 " full=(%d,%d,r=%d,b=%d) fullBase=%d"
    633                 " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d",
    634                 mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
    635                 full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
    636                 mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase);
    637             return mLastIntersects;
    638         }
    639         // is the text to the left of the selection start?
    640         if (baseLinesAgree(mStart, mStartBase, full, fullBase)
    641             && full.fLeft < mStart.fLeft) {
    642             if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2"
    643                 " full=(%d,%d,r=%d,b=%d) fullBase=%d"
    644                 " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
    645                 " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d",
    646                 mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
    647                 full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
    648                 mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
    649                 mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase);
    650             mStartExtra.join(full);
    651             return mLastIntersects;
    652         }
    653         // is the text to the right of the selection end?
    654         if (baseLinesAgree(mEnd, mEndBase, full, fullBase)
    655             && full.fRight > mEnd.fRight) {
    656             if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3"
    657                 " full=(%d,%d,r=%d,b=%d) fullBase=%d"
    658                 " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
    659                 " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
    660                 mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
    661                 full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
    662                 mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
    663                 mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
    664             mEndExtra.join(full);
    665             return mLastIntersects;
    666         }
    667         int spaceGap = SkFixedRound(minSpaceWidth() * 3);
    668         // should text to the left of the start be added to the selection bounds?
    669         if (!mStartExtra.isEmpty()) {
    670             if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
    671                 " mStartExtra=(%d,%d,r=%d,b=%d)",
    672                 mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
    673                 mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom);
    674             if (mStartExtra.fRight + spaceGap >= mStart.fLeft)
    675                 mSelectRect.join(mStartExtra);
    676             mStartExtra.setEmpty();
    677         }
    678         // should text to the right of the end be added to the selection bounds?
    679         if (!mEndExtra.isEmpty()) {
    680             if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
    681                 " mEndExtra=(%d,%d,r=%d,b=%d)",
    682                 mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
    683                 mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
    684             if (mEndExtra.fLeft - spaceGap <= mEnd.fRight)
    685                 mSelectRect.join(mEndExtra);
    686             mEndExtra.setEmpty();
    687         }
    688         bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase);
    689         bool adjacent = (full.fLeft - mLast.fRight) < spaceGap;
    690         // is this the first, or are there more characters on the same line?
    691         if (mLast.isEmpty() || (sameBaseLine && adjacent)) {
    692             if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION"
    693                 " full=(%d,%d,r=%d,b=%d) fullBase=%d"
    694                 " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
    695                 " mSelectRect=(%d,%d,r=%d,b=%d)",
    696                 full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
    697                 mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
    698                 mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
    699             mLast.join(full);
    700             mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
    701             return WAIT_FOR_INTERSECTION;
    702         }
    703         if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4"
    704             " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
    705             " full=(%d,%d,r=%d,b=%d) fullBase=%d"
    706             " mSelectRect=(%d,%d,r=%d,b=%d)"
    707             " mStartExtra=(%d,%d,r=%d,b=%d)"
    708             " mEndExtra=(%d,%d,r=%d,b=%d)",
    709             mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
    710             mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
    711             full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
    712             mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
    713             mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom,
    714             mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
    715         // after the caller determines what to do with the last collection,
    716         // start the collection over with full and fullBase.
    717         mCollectFull = true;
    718         return mLastIntersects;
    719     }
    720 
    721     bool resetLast(const SkIRect& full, int fullBase)
    722     {
    723         if (mCollectFull) {
    724             mLast = full;
    725             mLastBase = fullBase;
    726             mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
    727         } else {
    728             mLast.setEmpty();
    729             mLastBase = INT_MAX;
    730             mLastIntersects = false;
    731         }
    732         return mCollectFull;
    733     }
    734 
    735     void setFlippedState()
    736     {
    737         mSelectRect = mStart;
    738         mSelectRect.join(mEnd);
    739         DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)",
    740             mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
    741         mLast.setEmpty();
    742         mLastBase = INT_MAX;
    743         mLastIntersects = NO_INTERSECTION;
    744     }
    745 
    746     bool mCapture;
    747     bool mCollectFull;
    748     SkIRect mEnd;
    749     int mEndBase;
    750     SkIRect mEndExtra;
    751     bool mFlipped;
    752     SkIRect mLast;
    753     int mLastBase;
    754     int mLastIntersects;
    755     SkIRect mSelectRect;
    756     SkIRect mStart;
    757     SkIRect mStartExtra;
    758     int mStartBase;
    759 private:
    760     typedef CommonCheck INHERITED;
    761 
    762 };
    763 
    764 class MultilineBuilder : public BuilderCheck {
    765 public:
    766     MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end,
    767             int endBase, const SkIRect& area, SkRegion* region)
    768         : INHERITED(start, startBase, end, endBase, area)
    769         , mSelectRegion(region)
    770     {
    771         mFlipped = false;
    772     }
    773 
    774     void addLastToRegion() {
    775         if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)",
    776             mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
    777         mSelectRegion->op(mLast, SkRegion::kUnion_Op);
    778     }
    779 
    780     void finish() {
    781         if (!mFlipped || !mLastIntersects)
    782             return;
    783         addLastToRegion();
    784     }
    785 
    786     // return true if capture end was not found after capture begin
    787     bool flipped() {
    788         DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false");
    789         if (!mCapture)
    790             return false;
    791         mFlipped = true;
    792         setFlippedState();
    793         mSelectRegion->setEmpty();
    794         return true;
    795     }
    796 
    797     virtual bool onIRect(const SkIRect& rect) {
    798         SkIRect full;
    799         full.set(rect.fLeft, top(), rect.fRight, bottom());
    800         int fullBase = base();
    801         if (mFlipped) {
    802             int intersectType = checkFlipRect(full, fullBase);
    803             if (intersectType == LAST_INTERSECTION)
    804                 addLastToRegion();
    805             if (intersectType != WAIT_FOR_INTERSECTION)
    806                 resetLast(full, fullBase);
    807             return false;
    808         }
    809         if (full == mStart) {
    810             if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)",
    811                 full.fLeft, full.fTop, full.fRight, full.fBottom);
    812             mCapture = true;
    813         }
    814         if (mCapture) {
    815             bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase);
    816             if (sameLines)
    817                 mLast.join(full);
    818             if (!sameLines || full == mEnd) {
    819                 if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)",
    820                     mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
    821                 addLastToRegion();
    822                 mLast = full;
    823                 mLastBase = fullBase;
    824             }
    825         }
    826         if (full == mEnd) {
    827             if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)",
    828                 full.fLeft, full.fTop, full.fRight, full.fBottom);
    829             mCapture = false;
    830         }
    831         return false;
    832     }
    833 
    834 protected:
    835     SkRegion* mSelectRegion;
    836 private:
    837     typedef BuilderCheck INHERITED;
    838 };
    839 
    840 static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
    841 {
    842     return first->fTop < second->fTop;
    843 }
    844 
    845 class TextExtractor : public BuilderCheck {
    846 public:
    847     TextExtractor(const SkIRect& start, int startBase, const SkIRect& end,
    848         int endBase, const SkIRect& area, bool flipped)
    849         : INHERITED(start, startBase, end, endBase, area)
    850         , mSelectStartIndex(-1)
    851         , mSkipFirstSpace(true) // don't start with a space
    852     {
    853         mFlipped = flipped;
    854         if (flipped)
    855             setFlippedState();
    856     }
    857 
    858     void addCharacter(const SkBounder::GlyphRec& rec)
    859     {
    860         if (mSelectStartIndex < 0)
    861             mSelectStartIndex = mSelectText.count();
    862         if (!mSkipFirstSpace) {
    863             if (addNewLine(rec)) {
    864                 DBG_NAV_LOG("write new line");
    865                 *mSelectText.append() = '\n';
    866                 *mSelectText.append() = '\n';
    867             } else if (addSpace(rec)) {
    868                 DBG_NAV_LOG("write space");
    869                 *mSelectText.append() = ' ';
    870             }
    871         } else
    872             mSkipFirstSpace = false;
    873         recordGlyph(rec);
    874         finishGlyph();
    875         if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID,
    876             mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
    877         if (mLastUni) {
    878             uint16_t chars[2];
    879             size_t count = SkUTF16_FromUnichar(mLastUni, chars);
    880             *mSelectText.append() = chars[0];
    881             if (count == 2)
    882                 *mSelectText.append() = chars[1];
    883         }
    884     }
    885 
    886     void addLast()
    887     {
    888         *mSelectBounds.append() = mLast;
    889         *mSelectStart.append() = mSelectStartIndex;
    890         *mSelectEnd.append() = mSelectText.count();
    891     }
    892 
    893     /* Text characters are collected before it's been determined that the
    894        characters are part of the selection. The bounds describe valid parts
    895        of the selection, but the bounds are out of order.
    896 
    897        This sorts the characters by sorting the bounds, then copying the
    898        characters that were captured.
    899      */
    900     void finish()
    901     {
    902         if (mLastIntersects)
    903             addLast();
    904         Vector<SkIRect*> sortedBounds;
    905         SkTDArray<uint16_t> temp;
    906         int index;
    907         DBG_NAV_LOGD("mSelectBounds.count=%d text=%d", mSelectBounds.count(),
    908             mSelectText.count());
    909         for (index = 0; index < mSelectBounds.count(); index++)
    910             sortedBounds.append(&mSelectBounds[index]);
    911         std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds);
    912         int lastEnd = -1;
    913         for (index = 0; index < mSelectBounds.count(); index++) {
    914             int order = sortedBounds[index] - &mSelectBounds[0];
    915             int start = mSelectStart[order];
    916             int end = mSelectEnd[order];
    917             DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end,
    918                 mSelectBounds[order].fTop);
    919             int count = temp.count();
    920             if (count > 0 && temp[count - 1] != '\n' && start != lastEnd) {
    921                 // always separate paragraphs when original text is out of order
    922                 DBG_NAV_LOG("write new line");
    923                 *temp.append() = '\n';
    924                 *temp.append() = '\n';
    925             }
    926             temp.append(end - start, &mSelectText[start]);
    927             lastEnd = end;
    928         }
    929         mSelectText.swap(temp);
    930     }
    931 
    932     virtual bool onIRectGlyph(const SkIRect& rect,
    933         const SkBounder::GlyphRec& rec)
    934     {
    935         SkIRect full;
    936         full.set(rect.fLeft, top(), rect.fRight, bottom());
    937         int fullBase = base();
    938         if (mFlipped) {
    939             int intersectType = checkFlipRect(full, fullBase);
    940             if (WAIT_FOR_INTERSECTION == intersectType)
    941                 addCharacter(rec); // may not be copied
    942             else {
    943                 if (LAST_INTERSECTION == intersectType)
    944                     addLast();
    945                 else
    946                     mSkipFirstSpace = true;
    947                 mSelectStartIndex = -1;
    948                 if (resetLast(full, fullBase))
    949                     addCharacter(rec); // may not be copied
    950             }
    951             return false;
    952         }
    953         if (full == mStart)
    954             mCapture = true;
    955         if (mCapture)
    956             addCharacter(rec);
    957         else
    958             mSkipFirstSpace = true;
    959         if (full == mEnd)
    960             mCapture = false;
    961         return false;
    962     }
    963 
    964     WebCore::String text() {
    965         if (mFlipped)
    966             finish();
    967         // the text has been copied in visual order. Reverse as needed if
    968         // result contains right-to-left characters.
    969         const uint16_t* start = mSelectText.begin();
    970         const uint16_t* end = mSelectText.end();
    971         while (start < end) {
    972             SkUnichar ch = SkUTF16_NextUnichar(&start);
    973             WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch);
    974             if (WTF::Unicode::RightToLeftArabic == charDirection
    975                     || WTF::Unicode::RightToLeft == charDirection) {
    976                 WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count());
    977                 break;
    978             }
    979         }
    980         return WebCore::String(mSelectText.begin(), mSelectText.count());
    981     }
    982 
    983 protected:
    984     SkIRect mEmpty;
    985     SkTDArray<SkIRect> mSelectBounds;
    986     SkTDArray<int> mSelectEnd;
    987     SkTDArray<int> mSelectStart;
    988     int mSelectStartIndex;
    989     SkTDArray<uint16_t> mSelectText;
    990     bool mSkipFirstSpace;
    991 private:
    992     typedef BuilderCheck INHERITED;
    993 };
    994 
    995 class TextCanvas : public SkCanvas {
    996 public:
    997 
    998     TextCanvas(CommonCheck* bounder, const SkIRect& area)
    999             : mBounder(*bounder) {
   1000         setBounder(bounder);
   1001         SkBitmap bitmap;
   1002         bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
   1003             area.height());
   1004         setBitmapDevice(bitmap);
   1005         translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
   1006     }
   1007 
   1008     virtual ~TextCanvas() {
   1009         setBounder(NULL);
   1010     }
   1011 
   1012     virtual void drawPaint(const SkPaint& paint) {
   1013     }
   1014 
   1015     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
   1016                             const SkPaint& paint) {
   1017     }
   1018 
   1019     virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
   1020     }
   1021 
   1022     virtual void drawPath(const SkPath& path, const SkPaint& paint) {
   1023     }
   1024 
   1025     virtual void commonDrawBitmap(const SkBitmap& bitmap,
   1026                               const SkMatrix& matrix, const SkPaint& paint) {
   1027     }
   1028 
   1029     virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
   1030                             const SkPaint* paint = NULL) {
   1031     }
   1032 
   1033     virtual void drawText(const void* text, size_t byteLength, SkScalar x,
   1034                           SkScalar y, const SkPaint& paint) {
   1035         mBounder.setUp(paint, getTotalMatrix(), y, text);
   1036         SkCanvas::drawText(text, byteLength, x, y, paint);
   1037     }
   1038 
   1039     virtual void drawPosTextH(const void* text, size_t byteLength,
   1040                               const SkScalar xpos[], SkScalar constY,
   1041                               const SkPaint& paint) {
   1042         mBounder.setUp(paint, getTotalMatrix(), constY, text);
   1043         SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
   1044     }
   1045 
   1046     virtual void drawVertices(VertexMode vmode, int vertexCount,
   1047                               const SkPoint vertices[], const SkPoint texs[],
   1048                               const SkColor colors[], SkXfermode* xmode,
   1049                               const uint16_t indices[], int indexCount,
   1050                               const SkPaint& paint) {
   1051     }
   1052 
   1053     CommonCheck& mBounder;
   1054 };
   1055 
   1056 static bool buildSelection(const SkPicture& picture, const SkIRect& area,
   1057         const SkIRect& selStart, int startBase,
   1058         const SkIRect& selEnd, int endBase, SkRegion* region)
   1059 {
   1060     DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)"
   1061         " selEnd=(%d, %d, %d, %d)",
   1062         area.fLeft, area.fTop, area.fRight, area.fBottom,
   1063         selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom,
   1064         selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom);
   1065     MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region);
   1066     TextCanvas checker(&builder, area);
   1067     checker.drawPicture(const_cast<SkPicture&>(picture));
   1068     bool flipped = builder.flipped();
   1069     if (flipped) {
   1070         TextCanvas checker(&builder, area);
   1071         checker.drawPicture(const_cast<SkPicture&>(picture));
   1072     }
   1073     builder.finish();
   1074     region->translate(area.fLeft, area.fTop);
   1075     return flipped;
   1076 }
   1077 
   1078 static SkIRect findClosest(FirstCheck& _check, const SkPicture& picture,
   1079         const SkIRect& area, int* base)
   1080 {
   1081     DBG_NAV_LOGD("area=(%d, %d, %d, %d)", area.fLeft, area.fTop,
   1082         area.fRight, area.fBottom);
   1083     TextCanvas checker(&_check, area);
   1084     checker.drawPicture(const_cast<SkPicture&>(picture));
   1085     _check.finishGlyph();
   1086     return _check.adjustedBounds(area, base);
   1087 }
   1088 
   1089 static SkIRect findEdge(const SkPicture& picture, const SkIRect& area,
   1090         int x, int y, bool left, int* base)
   1091 {
   1092     SkIRect result;
   1093     result.setEmpty();
   1094     FirstCheck center(x, y, area);
   1095     center.setRecordGlyph();
   1096     int closestBase;
   1097     SkIRect closest = findClosest(center, picture, area, &closestBase);
   1098     closest.inset(-TOUCH_SLOP, -TOUCH_SLOP);
   1099     if (!closest.contains(x, y)) {
   1100         DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d",
   1101             closest.fLeft, closest.fTop, closest.fRight, closest.fBottom,
   1102             area.fLeft, area.fTop, area.fRight, area.fBottom, x, y);
   1103         return result;
   1104     }
   1105     EdgeCheck edge(x, y, area, center, left);
   1106     do { // detect left or right until there's a gap
   1107         DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d",
   1108             &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom);
   1109         TextCanvas checker(&edge, area);
   1110         checker.drawPicture(const_cast<SkPicture&>(picture));
   1111         edge.finishGlyph();
   1112         if (!edge.adjacent()) {
   1113             DBG_NAV_LOG("adjacent break");
   1114             break;
   1115         }
   1116         int nextBase;
   1117         const SkIRect& next = edge.bestBounds(&nextBase);
   1118         if (next.isEmpty()) {
   1119             DBG_NAV_LOG("empty");
   1120             break;
   1121         }
   1122         if (result == next) {
   1123             DBG_NAV_LOG("result == next");
   1124             break;
   1125         }
   1126         *base = nextBase;
   1127         result = next;
   1128         edge.shiftStart(result);
   1129     } while (true);
   1130     if (!result.isEmpty()) {
   1131         *base += area.fTop;
   1132         result.offset(area.fLeft, area.fTop);
   1133     }
   1134     return result;
   1135 }
   1136 
   1137 static SkIRect findFirst(const SkPicture& picture, int* base)
   1138 {
   1139     FindFirst finder(picture.width(), picture.height());
   1140     SkIRect area;
   1141     area.set(0, 0, picture.width(), picture.height());
   1142     TextCanvas checker(&finder, area);
   1143     checker.drawPicture(const_cast<SkPicture&>(picture));
   1144     return finder.bestBounds(base);
   1145 }
   1146 
   1147 static SkIRect findLast(const SkPicture& picture, int* base)
   1148 {
   1149     FindLast finder(picture.width(), picture.height());
   1150     SkIRect area;
   1151     area.set(0, 0, picture.width(), picture.height());
   1152     TextCanvas checker(&finder, area);
   1153     checker.drawPicture(const_cast<SkPicture&>(picture));
   1154     return finder.bestBounds(base);
   1155 }
   1156 
   1157 static SkIRect findLeft(const SkPicture& picture, const SkIRect& area,
   1158         int x, int y, int* base)
   1159 {
   1160     return findEdge(picture, area, x, y, true, base);
   1161 }
   1162 
   1163 static SkIRect findRight(const SkPicture& picture, const SkIRect& area,
   1164         int x, int y, int* base)
   1165 {
   1166     return findEdge(picture, area, x, y, false, base);
   1167 }
   1168 
   1169 static WebCore::String text(const SkPicture& picture, const SkIRect& area,
   1170         const SkIRect& start, int startBase, const SkIRect& end,
   1171         int endBase, bool flipped)
   1172 {
   1173     TextExtractor extractor(start, startBase, end, endBase, area, flipped);
   1174     TextCanvas checker(&extractor, area);
   1175     checker.drawPicture(const_cast<SkPicture&>(picture));
   1176     return extractor.text();
   1177 }
   1178 
   1179 #define CONTROL_OFFSET 0
   1180 #define CONTROL_NOTCH 19
   1181 #define CONTROL_HEIGHT 35
   1182 #define CONTROL_WIDTH 21
   1183 #define STROKE_WIDTH 2.0f
   1184 #define STROKE_OUTSET 1.0f
   1185 #define STROKE_NOTCH_R 2.2f
   1186 #define STROKE_NOTCH_L 1.7f
   1187 #define DROP_HEIGHT 4
   1188 
   1189 #define STROKE_COLOR 0x90000000
   1190 #define FILL_GRADIENT_TOP 0xD0F8DFA0
   1191 #define FILL_GRADIENT_BOTTOM 0xD0FFEFEF
   1192 #define DROP_GRADIENT_TOP 0x50000000
   1193 #define DROP_GRADIENT_BOTTOM 0x00000000
   1194 
   1195 #define SLOP 20
   1196 
   1197 SelectText::SelectText()
   1198 {
   1199     reset();
   1200     SkPaint paint;
   1201 
   1202     SkPath startFillPath;
   1203     startFillPath.moveTo(-CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH);
   1204     startFillPath.lineTo(-CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
   1205     startFillPath.lineTo(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
   1206     startFillPath.lineTo(-STROKE_OUTSET, CONTROL_OFFSET + STROKE_NOTCH_R);
   1207     startFillPath.close();
   1208     SkPath startStrokePath;
   1209     startStrokePath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
   1210     startStrokePath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
   1211     startStrokePath.lineTo(0, CONTROL_HEIGHT);
   1212     startStrokePath.lineTo(0, CONTROL_OFFSET);
   1213     startStrokePath.close();
   1214     SkPoint gradientLine[] = {{0, 0}, {0, CONTROL_HEIGHT}};
   1215     SkColor gradientColors[] = {FILL_GRADIENT_TOP, FILL_GRADIENT_BOTTOM};
   1216     SkShader* fillGradient = SkGradientShader::CreateLinear(gradientLine,
   1217         gradientColors, 0, 2, SkShader::kClamp_TileMode);
   1218     SkPoint dropLine[] = {{0, CONTROL_HEIGHT}, {0, CONTROL_HEIGHT + DROP_HEIGHT}};
   1219     SkColor dropColors[] = {DROP_GRADIENT_TOP, DROP_GRADIENT_BOTTOM};
   1220     SkShader* dropGradient = SkGradientShader::CreateLinear(dropLine,
   1221         dropColors, 0, 2, SkShader::kClamp_TileMode);
   1222     SkRect startDropRect = {-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT,
   1223         STROKE_OUTSET, CONTROL_HEIGHT + DROP_HEIGHT};
   1224 
   1225     SkCanvas* canvas = m_startControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT + DROP_HEIGHT);
   1226     paint.setAntiAlias(true);
   1227     paint.setStyle(SkPaint::kFill_Style);
   1228     paint.setShader(fillGradient);
   1229     canvas->drawPath(startFillPath, paint);
   1230     paint.setShader(0);
   1231     paint.setStyle(SkPaint::kStroke_Style);
   1232     paint.setColor(STROKE_COLOR);
   1233     paint.setStrokeWidth(STROKE_WIDTH);
   1234     canvas->drawPath(startStrokePath, paint);
   1235     paint.setStyle(SkPaint::kFill_Style);
   1236     paint.setColor(0xff000000);
   1237     paint.setShader(dropGradient);
   1238     canvas->drawRect(startDropRect, paint);
   1239     m_startControl.endRecording();
   1240 
   1241     SkPath endFillPath;
   1242     endFillPath.moveTo(STROKE_OUTSET, CONTROL_OFFSET + STROKE_NOTCH_R);
   1243     endFillPath.lineTo(STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
   1244     endFillPath.lineTo(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
   1245     endFillPath.lineTo(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH);
   1246     endFillPath.close();
   1247     SkPath endStrokePath;
   1248     endStrokePath.moveTo(0, CONTROL_OFFSET);
   1249     endStrokePath.lineTo(0, CONTROL_HEIGHT);
   1250     endStrokePath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
   1251     endStrokePath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
   1252     endStrokePath.close();
   1253     SkRect endDropRect = {-STROKE_OUTSET, CONTROL_HEIGHT,
   1254         CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + DROP_HEIGHT};
   1255 
   1256     canvas = m_endControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
   1257     paint.setColor(0xff000000);
   1258     paint.setStyle(SkPaint::kFill_Style);
   1259     paint.setShader(fillGradient);
   1260     canvas->drawPath(endFillPath, paint);
   1261     paint.setShader(0);
   1262     paint.setStyle(SkPaint::kStroke_Style);
   1263     paint.setColor(STROKE_COLOR);
   1264     paint.setStrokeWidth(STROKE_WIDTH);
   1265     canvas->drawPath(endStrokePath, paint);
   1266     paint.setStyle(SkPaint::kFill_Style);
   1267     paint.setColor(0xff000000);
   1268     paint.setShader(dropGradient);
   1269     canvas->drawRect(endDropRect, paint);
   1270     m_endControl.endRecording();
   1271     fillGradient->safeUnref();
   1272     dropGradient->safeUnref();
   1273     m_picture = 0;
   1274 }
   1275 
   1276 void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer)
   1277 {
   1278     // Gmail makes layers appear dynamically the page scrolls. The picture
   1279     // recorded when the selection begins is confused by the pictures seen
   1280     // in subsequent layers. To work around this, only allow text selection
   1281     // in the main picture.
   1282     if (layer->uniqueId() != -1)
   1283         return;
   1284     // FIXME: layer may not own the original selected picture
   1285     m_picture = layer->picture();
   1286     if (!m_picture)
   1287         return;
   1288     DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d", m_extendSelection, m_drawPointer);
   1289     if (m_extendSelection)
   1290         drawSelectionRegion(canvas);
   1291     if (m_drawPointer)
   1292         drawSelectionPointer(canvas);
   1293 }
   1294 
   1295 void SelectText::drawSelectionPointer(SkCanvas* canvas)
   1296 {
   1297     SkPath path;
   1298     if (m_extendSelection)
   1299         getSelectionCaret(&path);
   1300     else
   1301         getSelectionArrow(&path);
   1302     SkPaint paint;
   1303     paint.setAntiAlias(true);
   1304     paint.setStyle(SkPaint::kStroke_Style);
   1305     paint.setColor(SK_ColorBLACK);
   1306     SkPixelXorXfermode xorMode(SK_ColorWHITE);
   1307     if (m_extendSelection)
   1308         paint.setXfermode(&xorMode);
   1309     else
   1310         paint.setStrokeWidth(SK_Scalar1 * 2);
   1311     int sc = canvas->save();
   1312     canvas->scale(m_inverseScale, m_inverseScale);
   1313     canvas->translate(m_selectX, m_selectY);
   1314     canvas->drawPath(path, paint);
   1315     if (!m_extendSelection) {
   1316         paint.setStyle(SkPaint::kFill_Style);
   1317         paint.setColor(SK_ColorWHITE);
   1318         canvas->drawPath(path, paint);
   1319     }
   1320     canvas->restoreToCount(sc);
   1321 }
   1322 
   1323 void SelectText::drawSelectionRegion(SkCanvas* canvas)
   1324 {
   1325     m_selRegion.setEmpty();
   1326     SkRect visBounds;
   1327     if (!canvas->getClipBounds(&visBounds, SkCanvas::kAA_EdgeType))
   1328         return;
   1329     SkIRect ivisBounds;
   1330     visBounds.round(&ivisBounds);
   1331     ivisBounds.join(m_selStart);
   1332     ivisBounds.join(m_selEnd);
   1333     DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
   1334         m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
   1335         m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
   1336     m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
   1337         m_selEnd, m_endBase, &m_selRegion);
   1338     SkPath path;
   1339     m_selRegion.getBoundaryPath(&path);
   1340     path.setFillType(SkPath::kEvenOdd_FillType);
   1341 
   1342     SkPaint paint;
   1343     paint.setAntiAlias(true);
   1344     paint.setColor(SkColorSetARGB(0x80, 0xFF, 0xA8, 0x00));
   1345     canvas->drawPath(path, paint);
   1346     // experiment to draw touchable controls that resize the selection
   1347     canvas->save();
   1348     canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
   1349     canvas->drawPicture(m_startControl);
   1350     canvas->restore();
   1351     canvas->save();
   1352     canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
   1353     canvas->drawPicture(m_endControl);
   1354     canvas->restore();
   1355 }
   1356 
   1357 void SelectText::extendSelection(const SkPicture* picture, int x, int y)
   1358 {
   1359     if (!picture)
   1360         return;
   1361     SkIRect clipRect = m_visibleRect;
   1362     int base;
   1363     if (m_startSelection) {
   1364         if (!clipRect.contains(x, y)
   1365                 || !clipRect.contains(m_original.fX, m_original.fY)) {
   1366             clipRect.set(m_original.fX, m_original.fY, x, y);
   1367             clipRect.sort();
   1368             clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
   1369         }
   1370         DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft,
   1371             clipRect.fTop, clipRect.fRight, clipRect.fBottom);
   1372         m_picture = picture;
   1373         FirstCheck center(m_original.fX, m_original.fY, clipRect);
   1374         m_selStart = m_selEnd = findClosest(center, *picture, clipRect, &base);
   1375         m_startBase = m_endBase = base;
   1376         m_startSelection = false;
   1377         m_extendSelection = true;
   1378         m_original.fX = m_original.fY = 0;
   1379     } else if (picture != m_picture)
   1380         return;
   1381     x -= m_original.fX;
   1382     y -= m_original.fY;
   1383     if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) {
   1384         clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y);
   1385         clipRect.sort();
   1386         clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
   1387     }
   1388     DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d)", clipRect.fLeft,
   1389         clipRect.fTop, clipRect.fRight, clipRect.fBottom);
   1390     FirstCheck extension(x, y, clipRect);
   1391     SkIRect found = findClosest(extension, *picture, clipRect, &base);
   1392     DBG_NAV_LOGD("pic=%p x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
   1393         " m_extendSelection=%s",
   1394         picture, x, y, m_startSelection ? "true" : "false",
   1395         m_hitTopLeft ? "m_selStart" : "m_selEnd",
   1396         found.fLeft, found.fTop, found.fRight, found.fBottom,
   1397         m_extendSelection ? "true" : "false");
   1398     if (m_hitTopLeft) {
   1399         m_startBase = base;
   1400         m_selStart = found;
   1401     } else {
   1402         m_endBase = base;
   1403         m_selEnd = found;
   1404     }
   1405     swapAsNeeded();
   1406 }
   1407 
   1408 const String SelectText::getSelection()
   1409 {
   1410     if (!m_picture)
   1411         return String();
   1412     SkIRect clipRect;
   1413     clipRect.set(0, 0, m_picture->width(), m_picture->height());
   1414     String result = text(*m_picture, clipRect, m_selStart, m_startBase,
   1415         m_selEnd, m_endBase, m_flipped);
   1416     DBG_NAV_LOGD("clip=(%d,%d,%d,%d)"
   1417         " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
   1418         clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom,
   1419         m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
   1420         m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
   1421     DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString
   1422     return result;
   1423 }
   1424 
   1425 void SelectText::getSelectionArrow(SkPath* path)
   1426 {
   1427     const int arrow[] = {
   1428         0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11
   1429     };
   1430     for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2)
   1431         path->lineTo(arrow[index], arrow[index + 1]);
   1432     path->close();
   1433 }
   1434 
   1435 void SelectText::getSelectionCaret(SkPath* path)
   1436 {
   1437     SkScalar height = m_selStart.fBottom - m_selStart.fTop;
   1438     SkScalar dist = height / 4;
   1439     path->moveTo(0, -height / 2);
   1440     path->rLineTo(0, height);
   1441     path->rLineTo(-dist, dist);
   1442     path->rMoveTo(0, -0.5f);
   1443     path->rLineTo(dist * 2, 0);
   1444     path->rMoveTo(0, 0.5f);
   1445     path->rLineTo(-dist, -dist);
   1446 }
   1447 
   1448 bool SelectText::hitCorner(int cx, int cy, int x, int y) const
   1449 {
   1450     SkIRect test;
   1451     test.set(cx, cy, cx, cy);
   1452     test.inset(-SLOP, -SLOP);
   1453     return test.contains(x, y);
   1454 }
   1455 
   1456 bool SelectText::hitSelection(int x, int y) const
   1457 {
   1458     int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
   1459     int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
   1460     if (hitCorner(left, top, x, y))
   1461         return true;
   1462     int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
   1463     int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
   1464     if (hitCorner(right, bottom, x, y))
   1465         return true;
   1466     SkIRect test;
   1467     test.set(x - CONTROL_WIDTH, y - CONTROL_HEIGHT, x + CONTROL_WIDTH,
   1468         y + CONTROL_HEIGHT);
   1469     return m_selRegion.intersects(test);
   1470 }
   1471 
   1472 void SelectText::moveSelection(const SkPicture* picture, int x, int y)
   1473 {
   1474     if (!picture)
   1475         return;
   1476     SkIRect clipRect = m_visibleRect;
   1477     clipRect.join(m_selStart);
   1478     clipRect.join(m_selEnd);
   1479     if (!m_extendSelection)
   1480         m_picture = picture;
   1481     FirstCheck center(x, y, clipRect);
   1482     int base;
   1483     SkIRect found = findClosest(center, *picture, clipRect, &base);
   1484     if (m_hitTopLeft || !m_extendSelection) {
   1485         m_startBase = base;
   1486         m_selStart = found;
   1487     }
   1488     if (!m_hitTopLeft || !m_extendSelection) {
   1489         m_endBase = base;
   1490         m_selEnd = found;
   1491     }
   1492     swapAsNeeded();
   1493     DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)"
   1494         " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false",
   1495         m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
   1496         m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
   1497 }
   1498 
   1499 void SelectText::reset()
   1500 {
   1501     DBG_NAV_LOG("m_extendSelection=false");
   1502     m_selStart.setEmpty();
   1503     m_selEnd.setEmpty();
   1504     m_extendSelection = false;
   1505     m_startSelection = false;
   1506 }
   1507 
   1508 void SelectText::selectAll(const SkPicture* picture)
   1509 {
   1510     m_selStart = findFirst(*picture, &m_startBase);
   1511     m_selEnd = findLast(*picture, &m_endBase);
   1512     m_extendSelection = true;
   1513 }
   1514 
   1515 int SelectText::selectionX() const
   1516 {
   1517     return m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight;
   1518 }
   1519 
   1520 int SelectText::selectionY() const
   1521 {
   1522     const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
   1523     return (rect.fTop + rect.fBottom) >> 1;
   1524 }
   1525 
   1526 bool SelectText::startSelection(int x, int y)
   1527 {
   1528     m_original.fX = x;
   1529     m_original.fY = y;
   1530     if (m_selStart.isEmpty()) {
   1531         DBG_NAV_LOGD("empty start x=%d y=%d", x, y);
   1532         m_startSelection = true;
   1533         return true;
   1534     }
   1535     int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
   1536     int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
   1537     m_hitTopLeft = hitCorner(left, top, x, y);
   1538     int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
   1539     int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
   1540     bool hitBottomRight = hitCorner(right, bottom, x, y);
   1541     DBG_NAV_LOGD("left=%d top=%d right=%d bottom=%d x=%d y=%d", left, top,
   1542         right, bottom, x, y);
   1543     if (m_hitTopLeft && (!hitBottomRight || y - top < bottom - y)) {
   1544         DBG_NAV_LOG("hit top left");
   1545         m_original.fX -= left;
   1546         m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1;
   1547     } else if (hitBottomRight) {
   1548         DBG_NAV_LOG("hit bottom right");
   1549         m_original.fX -= right;
   1550         m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
   1551     }
   1552     return m_hitTopLeft || hitBottomRight;
   1553 }
   1554 
   1555 /* selects the word at (x, y)
   1556 * a word is normally delimited by spaces
   1557 * a string of digits (even with inside spaces) is a word (for phone numbers)
   1558 * FIXME: digit find isn't implemented yet
   1559 * returns true if a word was selected
   1560 */
   1561 bool SelectText::wordSelection(const SkPicture* picture)
   1562 {
   1563     int x = m_selStart.fLeft;
   1564     int y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
   1565     SkIRect clipRect = m_visibleRect;
   1566     clipRect.fLeft -= m_visibleRect.width() >> 1;
   1567     int base;
   1568     SkIRect left = findLeft(*picture, clipRect, x, y, &base);
   1569     if (!left.isEmpty()) {
   1570         m_startBase = base;
   1571         m_selStart = left;
   1572     }
   1573     x = m_selEnd.fRight;
   1574     y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
   1575     clipRect = m_visibleRect;
   1576     clipRect.fRight += m_visibleRect.width() >> 1;
   1577     SkIRect right = findRight(*picture, clipRect, x, y, &base);
   1578     if (!right.isEmpty()) {
   1579         m_endBase = base;
   1580         m_selEnd = right;
   1581     }
   1582     DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
   1583         m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
   1584         m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
   1585     if (!left.isEmpty() || !right.isEmpty()) {
   1586         m_extendSelection = true;
   1587         return true;
   1588     }
   1589     return false;
   1590 }
   1591 
   1592 void SelectText::swapAsNeeded()
   1593 {
   1594     if (m_selStart.fTop >= (m_selEnd.fTop + m_selEnd.fBottom) >> 1
   1595             || (m_selEnd.fTop < (m_selStart.fTop + m_selStart.fBottom) >> 1
   1596             && m_selStart.fRight > m_selEnd.fLeft))
   1597     {
   1598         SkTSwap(m_startBase, m_endBase);
   1599         SkTSwap(m_selStart, m_selEnd);
   1600         m_hitTopLeft ^= true;
   1601         DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false");
   1602     }
   1603 }
   1604 
   1605 }
   1606