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 "config.h"
     29 #include "FindCanvas.h"
     30 #include "LayerAndroid.h"
     31 #include "IntRect.h"
     32 #include "SkBlurMaskFilter.h"
     33 #include "SkCornerPathEffect.h"
     34 #include "SkRect.h"
     35 
     36 #include <utils/Log.h>
     37 
     38 #define INTEGER_OUTSET 2
     39 
     40 namespace android {
     41 
     42 // MatchInfo methods
     43 ////////////////////////////////////////////////////////////////////////////////
     44 
     45 MatchInfo::MatchInfo() {
     46     m_picture = 0;
     47 }
     48 
     49 MatchInfo::~MatchInfo() {
     50     m_picture->safeUnref();
     51 }
     52 
     53 MatchInfo::MatchInfo(const MatchInfo& src) {
     54     m_layerId = src.m_layerId;
     55     m_location = src.m_location;
     56     m_picture = src.m_picture;
     57     m_picture->safeRef();
     58 }
     59 
     60 void MatchInfo::set(const SkRegion& region, SkPicture* pic, int layerId) {
     61     m_picture->safeUnref();
     62     m_layerId = layerId;
     63     m_location = region;
     64     m_picture = pic;
     65     SkASSERT(pic);
     66     pic->ref();
     67 }
     68 
     69 // GlyphSet methods
     70 ////////////////////////////////////////////////////////////////////////////////
     71 
     72 GlyphSet::GlyphSet(const SkPaint& paint, const UChar* lower, const UChar* upper,
     73             size_t byteLength) {
     74     SkPaint clonePaint(paint);
     75     clonePaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
     76     mTypeface = paint.getTypeface();
     77     mCount = clonePaint.textToGlyphs(lower, byteLength, NULL);
     78     if (mCount > MAX_STORAGE_COUNT) {
     79         mLowerGlyphs = new uint16_t[2*mCount];
     80     } else {
     81         mLowerGlyphs = &mStorage[0];
     82     }
     83     // Use one array, and have mUpperGlyphs point to a portion of it,
     84     // so that we can reduce the number of new/deletes
     85     mUpperGlyphs = mLowerGlyphs + mCount;
     86     int count2 = clonePaint.textToGlyphs(lower, byteLength, mLowerGlyphs);
     87     SkASSERT(mCount == count2);
     88     count2 = clonePaint.textToGlyphs(upper, byteLength, mUpperGlyphs);
     89     SkASSERT(mCount == count2);
     90 }
     91 
     92 GlyphSet::~GlyphSet() {
     93     // Do not need to delete mTypeface, which is not owned by us.
     94     if (mCount > MAX_STORAGE_COUNT) {
     95         delete[] mLowerGlyphs;
     96     }   // Otherwise, we just used local storage space, so no need to delete
     97     // Also do not need to delete mUpperGlyphs, which simply points to a
     98     // part of mLowerGlyphs
     99 }
    100 
    101 GlyphSet::GlyphSet& GlyphSet::operator=(GlyphSet& src) {
    102     mTypeface = src.mTypeface;
    103     mCount = src.mCount;
    104     if (mCount > MAX_STORAGE_COUNT) {
    105         mLowerGlyphs = new uint16_t[2*mCount];
    106     } else {
    107         mLowerGlyphs = &mStorage[0];
    108     }
    109     memcpy(mLowerGlyphs, src.mLowerGlyphs, 2*mCount*sizeof(uint16_t));
    110     mUpperGlyphs = mLowerGlyphs + mCount;
    111     return *this;
    112 }
    113 
    114 bool GlyphSet::characterMatches(uint16_t c, int index) {
    115     SkASSERT(index < mCount && index >= 0);
    116     return c == mLowerGlyphs[index] || c == mUpperGlyphs[index];
    117 }
    118 
    119 // FindCanvas methods
    120 ////////////////////////////////////////////////////////////////////////////////
    121 
    122 FindCanvas::FindCanvas(int width, int height, const UChar* lower,
    123         const UChar* upper, size_t byteLength)
    124         : mLowerText(lower)
    125         , mUpperText(upper)
    126         , mLength(byteLength)
    127         , mNumFound(0) {
    128 
    129     setBounder(&mBounder);
    130     mOutset = -SkIntToScalar(INTEGER_OUTSET);
    131     mMatches = new WTF::Vector<MatchInfo>();
    132     mWorkingIndex = 0;
    133     mWorkingCanvas = 0;
    134     mWorkingPicture = 0;
    135 }
    136 
    137 FindCanvas::~FindCanvas() {
    138     setBounder(NULL);
    139     /* Just in case getAndClear was not called. */
    140     delete mMatches;
    141     mWorkingPicture->safeUnref();
    142 }
    143 
    144 // Each version of addMatch returns a rectangle for a match.
    145 // Not all of the parameters are used by each version.
    146 SkRect FindCanvas::addMatchNormal(int index,
    147         const SkPaint& paint, int count, const uint16_t* glyphs,
    148         const SkScalar pos[], SkScalar y) {
    149     const uint16_t* lineStart = glyphs - index;
    150     /* Use the original paint, since "text" is in glyphs */
    151     SkScalar before = paint.measureText(lineStart, index * sizeof(uint16_t), 0);
    152     SkRect rect;
    153     rect.fLeft = pos[0] + before;
    154     int countInBytes = count * sizeof(uint16_t);
    155     rect.fRight = paint.measureText(glyphs, countInBytes, 0) + rect.fLeft;
    156     SkPaint::FontMetrics fontMetrics;
    157     paint.getFontMetrics(&fontMetrics);
    158     SkScalar baseline = y;
    159     rect.fTop = baseline + fontMetrics.fAscent;
    160     rect.fBottom = baseline + fontMetrics.fDescent;
    161     const SkMatrix& matrix = getTotalMatrix();
    162     matrix.mapRect(&rect);
    163     // Add the text to our picture.
    164     SkCanvas* canvas = getWorkingCanvas();
    165     int saveCount = canvas->save();
    166     canvas->concat(matrix);
    167     canvas->drawText(glyphs, countInBytes, pos[0] + before, y, paint);
    168     canvas->restoreToCount(saveCount);
    169     return rect;
    170 }
    171 
    172 SkRect FindCanvas::addMatchPos(int index,
    173         const SkPaint& paint, int count, const uint16_t* glyphs,
    174         const SkScalar xPos[], SkScalar /* y */) {
    175     SkRect r;
    176     r.setEmpty();
    177     const SkPoint* temp = reinterpret_cast<const SkPoint*> (xPos);
    178     const SkPoint* points = &temp[index];
    179     int countInBytes = count * sizeof(uint16_t);
    180     SkPaint::FontMetrics fontMetrics;
    181     paint.getFontMetrics(&fontMetrics);
    182     // Need to check each character individually, since the heights may be
    183     // different.
    184     for (int j = 0; j < count; j++) {
    185         SkRect bounds;
    186         bounds.fLeft = points[j].fX;
    187         bounds.fRight = bounds.fLeft +
    188                 paint.measureText(&glyphs[j], sizeof(uint16_t), 0);
    189         SkScalar baseline = points[j].fY;
    190         bounds.fTop = baseline + fontMetrics.fAscent;
    191         bounds.fBottom = baseline + fontMetrics.fDescent;
    192         /* Accumulate and then add the resulting rect to mMatches */
    193         r.join(bounds);
    194     }
    195     SkMatrix matrix = getTotalMatrix();
    196     matrix.mapRect(&r);
    197     SkCanvas* canvas = getWorkingCanvas();
    198     int saveCount = canvas->save();
    199     canvas->concat(matrix);
    200     canvas->drawPosText(glyphs, countInBytes, points, paint);
    201     canvas->restoreToCount(saveCount);
    202     return r;
    203 }
    204 
    205 SkRect FindCanvas::addMatchPosH(int index,
    206         const SkPaint& paint, int count, const uint16_t* glyphs,
    207         const SkScalar position[], SkScalar constY) {
    208     SkRect r;
    209     // We only care about the positions starting at the index of our match
    210     const SkScalar* xPos = &position[index];
    211     // This assumes that the position array is monotonic increasing
    212     // The left bounds will be the position of the left most character
    213     r.fLeft = xPos[0];
    214     // The right bounds will be the position of the last character plus its
    215     // width
    216     int lastIndex = count - 1;
    217     r.fRight = paint.measureText(&glyphs[lastIndex], sizeof(uint16_t), 0)
    218             + xPos[lastIndex];
    219     // Grab font metrics to determine the top and bottom of the bounds
    220     SkPaint::FontMetrics fontMetrics;
    221     paint.getFontMetrics(&fontMetrics);
    222     r.fTop = constY + fontMetrics.fAscent;
    223     r.fBottom = constY + fontMetrics.fDescent;
    224     const SkMatrix& matrix = getTotalMatrix();
    225     matrix.mapRect(&r);
    226     SkCanvas* canvas = getWorkingCanvas();
    227     int saveCount = canvas->save();
    228     canvas->concat(matrix);
    229     canvas->drawPosTextH(glyphs, count * sizeof(uint16_t), xPos, constY, paint);
    230     canvas->restoreToCount(saveCount);
    231     return r;
    232 }
    233 
    234 void FindCanvas::drawLayers(LayerAndroid* layer) {
    235 #if USE(ACCELERATED_COMPOSITING)
    236     SkPicture* picture = layer->picture();
    237     if (picture) {
    238         setLayerId(layer->uniqueId());
    239         drawPicture(*picture);
    240     }
    241     for (int i = 0; i < layer->countChildren(); i++)
    242         drawLayers(layer->getChild(i));
    243 #endif
    244 }
    245 
    246 void FindCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
    247                           SkScalar y, const SkPaint& paint) {
    248     findHelper(text, byteLength, paint, &x, y, &FindCanvas::addMatchNormal);
    249 }
    250 
    251 void FindCanvas::drawPosText(const void* text, size_t byteLength,
    252                              const SkPoint pos[], const SkPaint& paint) {
    253     // Pass in the first y coordinate for y so that we can check to see whether
    254     // it is lower than the last draw call (to check if we are continuing to
    255     // another line).
    256     findHelper(text, byteLength, paint, (const SkScalar*) pos, pos[0].fY,
    257             &FindCanvas::addMatchPos);
    258 }
    259 
    260 void FindCanvas::drawPosTextH(const void* text, size_t byteLength,
    261                               const SkScalar xpos[], SkScalar constY,
    262                               const SkPaint& paint) {
    263     findHelper(text, byteLength, paint, xpos, constY,
    264             &FindCanvas::addMatchPosH);
    265 }
    266 
    267 /* The current behavior is to skip substring matches. This means that in the
    268  * string
    269  *      batbatbat
    270  * a search for
    271  *      batbat
    272  * will return 1 match.  If the desired behavior is to return 2 matches, define
    273  * INCLUDE_SUBSTRING_MATCHES to be 1.
    274  */
    275 #define INCLUDE_SUBSTRING_MATCHES 0
    276 
    277 // Need a quick way to know a maximum distance between drawText calls to know if
    278 // they are part of the same logical phrase when searching.  By crude
    279 // inspection, half the point size seems a good guess at the width of a space
    280 // character.
    281 static inline SkScalar approximateSpaceWidth(const SkPaint& paint) {
    282     return SkScalarHalf(paint.getTextSize());
    283 }
    284 
    285 void FindCanvas::findHelper(const void* text, size_t byteLength,
    286                           const SkPaint& paint, const SkScalar positions[],
    287                           SkScalar y,
    288                           SkRect (FindCanvas::*addMatch)(int index,
    289                           const SkPaint& paint, int count,
    290                           const uint16_t* glyphs,
    291                           const SkScalar positions[], SkScalar y)) {
    292     SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
    293     SkASSERT(mMatches);
    294     GlyphSet* glyphSet = getGlyphs(paint);
    295     const int count = glyphSet->getCount();
    296     int numCharacters = byteLength >> 1;
    297     const uint16_t* chars = (const uint16_t*) text;
    298     // This block will check to see if we are continuing from another line.  If
    299     // so, the user needs to have added a space, which we do not draw.
    300     if (mWorkingIndex) {
    301         SkPoint newY;
    302         getTotalMatrix().mapXY(0, y, &newY);
    303         SkIRect workingBounds = mWorkingRegion.getBounds();
    304         int newYInt = SkScalarRound(newY.fY);
    305         if (workingBounds.fTop > newYInt) {
    306             // The new text is above the working region, so we know it's not
    307             // a continuation.
    308             resetWorkingCanvas();
    309             mWorkingIndex = 0;
    310             mWorkingRegion.setEmpty();
    311         } else if (workingBounds.fBottom < newYInt) {
    312             // Now we know that this line is lower than our partial match.
    313             SkPaint clonePaint(paint);
    314             clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
    315             uint16_t space;
    316             clonePaint.textToGlyphs(" ", 1, &space);
    317             if (glyphSet->characterMatches(space, mWorkingIndex)) {
    318                 mWorkingIndex++;
    319                 if (mWorkingIndex == count) {
    320                     // We already know that it is not clipped out because we
    321                     // checked for that before saving the working region.
    322                     insertMatchInfo(mWorkingRegion);
    323 
    324                     resetWorkingCanvas();
    325                     mWorkingIndex = 0;
    326                     mWorkingRegion.setEmpty();
    327                     // We have found a match, so continue on this line from
    328                     // scratch.
    329                 }
    330             } else {
    331                 resetWorkingCanvas();
    332                 mWorkingIndex = 0;
    333                 mWorkingRegion.setEmpty();
    334             }
    335         }
    336         // If neither one is true, then we are likely continuing on the same
    337         // line, but are in a new draw call because the paint has changed.  In
    338         // this case, we can continue without adding a space.
    339     }
    340     // j is the position in the search text
    341     // Start off with mWorkingIndex in case we are continuing from a prior call
    342     int j = mWorkingIndex;
    343     // index is the position in the drawn text
    344     int index = 0;
    345     for ( ; index != numCharacters; index++) {
    346         if (glyphSet->characterMatches(chars[index], j)) {
    347             // The jth character in the search text matches the indexth position
    348             // in the drawn text, so increase j.
    349             j++;
    350             if (j != count) {
    351                 continue;
    352             }
    353             // The last count characters match, so we found the entire
    354             // search string.
    355             int remaining = count - mWorkingIndex;
    356             int matchIndex = index - remaining + 1;
    357             // Set up a pointer to the matching text in 'chars'.
    358             const uint16_t* glyphs = chars + matchIndex;
    359             SkRect rect = (this->*addMatch)(matchIndex, paint,
    360                     remaining, glyphs, positions, y);
    361             // We need an SkIRect for SkRegion operations.
    362             SkIRect iRect;
    363             rect.roundOut(&iRect);
    364             // If the rectangle is partially clipped, assume that the text is
    365             // not visible, so skip this match.
    366             if (getTotalClip().contains(iRect)) {
    367                 // Want to outset the drawn rectangle by the same amount as
    368                 // mOutset
    369                 iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET);
    370                 SkRegion regionToAdd(iRect);
    371                 if (!mWorkingRegion.isEmpty()) {
    372                     // If this is on the same line as our working region, make
    373                     // sure that they are close enough together that they are
    374                     // supposed to be part of the same text string.
    375                     // The width of two spaces has arbitrarily been chosen.
    376                     const SkIRect& workingBounds = mWorkingRegion.getBounds();
    377                     if (workingBounds.fTop <= iRect.fBottom &&
    378                             workingBounds.fBottom >= iRect.fTop &&
    379                             SkIntToScalar(iRect.fLeft - workingBounds.fRight) >
    380                             approximateSpaceWidth(paint)) {
    381                         index = -1;     // Will increase to 0 on next run
    382                         // In this case, we need to start from the beginning of
    383                         // the text being searched and our search term.
    384                         j = 0;
    385                         mWorkingIndex = 0;
    386                         mWorkingRegion.setEmpty();
    387                         continue;
    388                     }
    389                     // Add the mWorkingRegion, which contains rectangles from
    390                     // the previous line(s).
    391                     regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op);
    392                 }
    393                 insertMatchInfo(regionToAdd);
    394 #if INCLUDE_SUBSTRING_MATCHES
    395                 // Reset index to the location of the match and reset j to the
    396                 // beginning, so that on the next iteration of the loop, index
    397                 // will advance by 1 and we will compare the next character in
    398                 // chars to the first character in the GlyphSet.
    399                 index = matchIndex;
    400 #endif
    401             } else {
    402                 // This match was clipped out, so begin looking at the next
    403                 // character from our hidden match
    404                 index = matchIndex;
    405             }
    406             // Whether the clip contained it or not, we need to start over
    407             // with our recording canvas
    408             resetWorkingCanvas();
    409         } else {
    410             // Index needs to be set to index - j + 1.
    411             // This is a ridiculous case, but imagine the situation where the
    412             // user is looking for the string "jjog" in the drawText call for
    413             // "jjjog".  The first two letters match.  However, when the index
    414             // is 2, and we discover that 'o' and 'j' do not match, we should go
    415             // back to 1, where we do, in fact, have a match
    416             // FIXME: This does not work if (as in our example) "jj" is in one
    417             // draw call and "jog" is in the next.  Doing so would require a
    418             // stack, keeping track of multiple possible working indeces and
    419             // regions.  This is likely an uncommon case.
    420             index = index - j;  // index will be increased by one on the next
    421                                 // iteration
    422         }
    423         // We reach here in one of two cases:
    424         // 1) We just completed a match, so any working rectangle/index is no
    425         // longer needed, and we will start over from the beginning
    426         // 2) The glyphs do not match, so we start over at the beginning of
    427         // the search string.
    428         j = 0;
    429         mWorkingIndex = 0;
    430         mWorkingRegion.setEmpty();
    431     }
    432     // At this point, we have searched all of the text in the current drawText
    433     // call.
    434     // Keep track of a partial match that may start on this line.
    435     if (j > 0) {    // if j is greater than 0, we have a partial match
    436         int relativeCount = j - mWorkingIndex;  // Number of characters in this
    437                                                 // part of the match.
    438         int partialIndex = index - relativeCount; // Index that starts our
    439                                                   // partial match.
    440         const uint16_t* partialGlyphs = chars + partialIndex;
    441         SkRect partial = (this->*addMatch)(partialIndex, paint, relativeCount,
    442                 partialGlyphs, positions, y);
    443         partial.inset(mOutset, mOutset);
    444         SkIRect dest;
    445         partial.roundOut(&dest);
    446         // Only save a partial if it is in the current clip.
    447         if (getTotalClip().contains(dest)) {
    448             mWorkingRegion.op(dest, SkRegion::kUnion_Op);
    449             mWorkingIndex = j;
    450             return;
    451         }
    452     }
    453     // This string doesn't go into the next drawText, so reset our working
    454     // variables
    455     mWorkingRegion.setEmpty();
    456     mWorkingIndex = 0;
    457 }
    458 
    459 SkCanvas* FindCanvas::getWorkingCanvas() {
    460     if (!mWorkingPicture) {
    461         mWorkingPicture = new SkPicture;
    462         mWorkingCanvas = mWorkingPicture->beginRecording(0,0);
    463     }
    464     return mWorkingCanvas;
    465 }
    466 
    467 GlyphSet* FindCanvas::getGlyphs(const SkPaint& paint) {
    468     SkTypeface* typeface = paint.getTypeface();
    469     GlyphSet* end = mGlyphSets.end();
    470     for (GlyphSet* ptr = mGlyphSets.begin();ptr != end; ptr++) {
    471         if (ptr->getTypeface() == typeface) {
    472             return ptr;
    473         }
    474     }
    475 
    476     GlyphSet set(paint, mLowerText, mUpperText, mLength);
    477     *mGlyphSets.append() = set;
    478     return &(mGlyphSets.top());
    479 }
    480 
    481 void FindCanvas::insertMatchInfo(const SkRegion& region) {
    482     mNumFound++;
    483     mWorkingPicture->endRecording();
    484     MatchInfo matchInfo;
    485     mMatches->append(matchInfo);
    486     LOGD("%s region=%p pict=%p layer=%d", __FUNCTION__,
    487         &region, mWorkingPicture, mLayerId);
    488     mMatches->last().set(region, mWorkingPicture, mLayerId);
    489 }
    490 
    491 void FindCanvas::resetWorkingCanvas() {
    492     mWorkingPicture->unref();
    493     mWorkingPicture = 0;
    494     // Do not need to reset mWorkingCanvas itself because we only access it via
    495     // getWorkingCanvas.
    496 }
    497 
    498 // This function sets up the paints that are used to draw the matches.
    499 void FindOnPage::setUpFindPaint() {
    500     // Set up the foreground paint
    501     m_findPaint.setAntiAlias(true);
    502     const SkScalar roundiness = SkIntToScalar(2);
    503     SkCornerPathEffect* cornerEffect = new SkCornerPathEffect(roundiness);
    504     m_findPaint.setPathEffect(cornerEffect);
    505     m_findPaint.setARGB(255, 132, 190, 0);
    506 
    507     // Set up the background blur paint.
    508     m_findBlurPaint.setAntiAlias(true);
    509     m_findBlurPaint.setARGB(204, 0, 0, 0);
    510     m_findBlurPaint.setPathEffect(cornerEffect);
    511     cornerEffect->unref();
    512     SkMaskFilter* blurFilter = SkBlurMaskFilter::Create(SK_Scalar1,
    513             SkBlurMaskFilter::kNormal_BlurStyle);
    514     m_findBlurPaint.setMaskFilter(blurFilter)->unref();
    515     m_isFindPaintSetUp = true;
    516 }
    517 
    518 IntRect FindOnPage::currentMatchBounds() const {
    519     IntRect noBounds = IntRect(0, 0, 0, 0);
    520     if (!m_matches || !m_matches->size())
    521         return noBounds;
    522     MatchInfo& info = (*m_matches)[m_findIndex];
    523     // FIXME: this should test if the match in the layer is visible
    524     if (info.isInLayer())
    525         return noBounds;
    526     return info.getLocation().getBounds();
    527 }
    528 
    529 bool FindOnPage::currentMatchIsInLayer() const {
    530     if (!m_matches || !m_matches->size())
    531         return false;
    532     MatchInfo& info = (*m_matches)[m_findIndex];
    533     return info.isInLayer();
    534 }
    535 
    536 // This function is only used by findNext and setMatches.  In it, we store
    537 // upper left corner of the match specified by m_findIndex in
    538 // m_currentMatchLocation.
    539 void FindOnPage::storeCurrentMatchLocation() {
    540     SkASSERT(m_findIndex < m_matches->size());
    541     const SkIRect& bounds = (*m_matches)[m_findIndex].getLocation().getBounds();
    542     m_currentMatchLocation.set(bounds.fLeft, bounds.fTop);
    543     m_hasCurrentLocation = true;
    544 }
    545 
    546 // Put a cap on the number of matches to draw.  If the current page has more
    547 // matches than this, only draw the focused match.
    548 #define MAX_NUMBER_OF_MATCHES_TO_DRAW 101
    549 
    550 void FindOnPage::draw(SkCanvas* canvas, LayerAndroid* layer) {
    551     if (!m_hasCurrentLocation || !m_matches || !m_matches->size())
    552         return;
    553     int layerId = layer->uniqueId();
    554     if (m_findIndex >= m_matches->size())
    555         m_findIndex = 0;
    556     const MatchInfo& matchInfo = (*m_matches)[m_findIndex];
    557     const SkRegion& currentMatchRegion = matchInfo.getLocation();
    558 
    559     // Set up the paints used for drawing the matches
    560     if (!m_isFindPaintSetUp)
    561         setUpFindPaint();
    562 
    563     // Draw the current match
    564     if (matchInfo.layerId() == layerId) {
    565         drawMatch(currentMatchRegion, canvas, true);
    566         // Now draw the picture, so that it shows up on top of the rectangle
    567         int saveCount = canvas->save();
    568         SkPath matchPath;
    569         currentMatchRegion.getBoundaryPath(&matchPath);
    570         canvas->clipPath(matchPath);
    571         canvas->drawPicture(*matchInfo.getPicture());
    572         canvas->restoreToCount(saveCount);
    573     }
    574     // Draw the rest
    575     unsigned numberOfMatches = m_matches->size();
    576     if (numberOfMatches > 1
    577             && numberOfMatches < MAX_NUMBER_OF_MATCHES_TO_DRAW) {
    578         for(unsigned i = 0; i < numberOfMatches; i++) {
    579             // The current match has already been drawn
    580             if (i == m_findIndex)
    581                 continue;
    582             if ((*m_matches)[i].layerId() != layerId)
    583                 continue;
    584             const SkRegion& region = (*m_matches)[i].getLocation();
    585             // Do not draw matches which intersect the current one, or if it is
    586             // offscreen
    587             if (currentMatchRegion.intersects(region))
    588                 continue;
    589             SkRect bounds;
    590             bounds.set(region.getBounds());
    591             if (canvas->quickReject(bounds, SkCanvas::kAA_EdgeType))
    592                 continue;
    593             drawMatch(region, canvas, false);
    594         }
    595     }
    596 }
    597 
    598 // Draw the match specified by region to the canvas.
    599 void FindOnPage::drawMatch(const SkRegion& region, SkCanvas* canvas,
    600         bool focused)
    601 {
    602     // For the match which has focus, use a filled paint.  For the others, use
    603     // a stroked paint.
    604     if (focused) {
    605         m_findPaint.setStyle(SkPaint::kFill_Style);
    606         m_findBlurPaint.setStyle(SkPaint::kFill_Style);
    607     } else {
    608         m_findPaint.setStyle(SkPaint::kStroke_Style);
    609         m_findPaint.setStrokeWidth(SK_Scalar1);
    610         m_findBlurPaint.setStyle(SkPaint::kStroke_Style);
    611         m_findBlurPaint.setStrokeWidth(SkIntToScalar(2));
    612     }
    613     // Find the path for the current match
    614     SkPath matchPath;
    615     region.getBoundaryPath(&matchPath);
    616     // Offset the path for a blurred shadow
    617     SkPath blurPath;
    618     matchPath.offset(SK_Scalar1, SkIntToScalar(2), &blurPath);
    619     int saveCount = 0;
    620     if (!focused) {
    621         saveCount = canvas->save();
    622         canvas->clipPath(matchPath, SkRegion::kDifference_Op);
    623     }
    624     // Draw the blurred background
    625     canvas->drawPath(blurPath, m_findBlurPaint);
    626     if (!focused)
    627         canvas->restoreToCount(saveCount);
    628     // Draw the foreground
    629     canvas->drawPath(matchPath, m_findPaint);
    630 }
    631 
    632 void FindOnPage::findNext(bool forward)
    633 {
    634     if (!m_matches || !m_matches->size() || !m_hasCurrentLocation)
    635         return;
    636     if (forward) {
    637         m_findIndex++;
    638         if (m_findIndex == m_matches->size())
    639             m_findIndex = 0;
    640     } else {
    641         if (m_findIndex == 0) {
    642             m_findIndex = m_matches->size() - 1;
    643         } else {
    644             m_findIndex--;
    645         }
    646     }
    647     storeCurrentMatchLocation();
    648 }
    649 
    650 // With this call, WebView takes ownership of matches, and is responsible for
    651 // deleting it.
    652 void FindOnPage::setMatches(WTF::Vector<MatchInfo>* matches)
    653 {
    654     if (m_matches)
    655         delete m_matches;
    656     m_matches = matches;
    657     if (m_matches->size()) {
    658         if (m_hasCurrentLocation) {
    659             for (unsigned i = 0; i < m_matches->size(); i++) {
    660                 const SkIRect& rect = (*m_matches)[i].getLocation().getBounds();
    661                 if (rect.fLeft == m_currentMatchLocation.fX
    662                         && rect.fTop == m_currentMatchLocation.fY) {
    663                     m_findIndex = i;
    664                     return;
    665                 }
    666             }
    667         }
    668         // If we did not have a stored location, or if we were unable to restore
    669         // it, store the new one.
    670         m_findIndex = 0;
    671         storeCurrentMatchLocation();
    672     } else {
    673         m_hasCurrentLocation = false;
    674     }
    675 }
    676 
    677 }
    678 
    679