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