• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MatchInfo()47 MatchInfo::MatchInfo() {
48     m_picture = 0;
49 }
50 
~MatchInfo()51 MatchInfo::~MatchInfo() {
52     SkSafeUnref(m_picture);
53 }
54 
MatchInfo(const MatchInfo & src)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 
set(const SkRegion & region,SkPicture * pic,int layerId)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 
GlyphSet(const SkPaint & paint,const UChar * lower,const UChar * upper,size_t byteLength)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 
~GlyphSet()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 
operator =(GlyphSet & src)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 
characterMatches(uint16_t c,int index)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 
FindCanvas(int width,int height,const UChar * lower,const UChar * upper,size_t byteLength)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 
~FindCanvas()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.
addMatchNormal(int index,const SkPaint & paint,int count,const uint16_t * glyphs,const SkScalar pos[],SkScalar y)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 
addMatchPos(int index,const SkPaint & paint,int count,const uint16_t * glyphs,const SkScalar xPos[],SkScalar)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 
addMatchPosH(int index,const SkPaint & paint,int count,const uint16_t * glyphs,const SkScalar position[],SkScalar constY)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 
drawLayers(LayerAndroid * layer)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 
drawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)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 
drawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)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 
drawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)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.
approximateSpaceWidth(const SkPaint & paint)304 static inline SkScalar approximateSpaceWidth(const SkPaint& paint) {
305     return SkScalarHalf(paint.getTextSize());
306 }
307 
findHelper(const void * text,size_t byteLength,const SkPaint & paint,const SkScalar positions[],SkScalar y,SkRect (FindCanvas::* addMatch)(int index,const SkPaint & paint,int count,const uint16_t * glyphs,const SkScalar positions[],SkScalar y))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 
getWorkingCanvas()471 SkCanvas* FindCanvas::getWorkingCanvas() {
472     if (!mWorkingPicture) {
473         mWorkingPicture = new SkPicture;
474         mWorkingCanvas = mWorkingPicture->beginRecording(0,0);
475     }
476     return mWorkingCanvas;
477 }
478 
getGlyphs(const SkPaint & paint)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 
insertMatchInfo(const SkRegion & region)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 
resetWorkingCanvas()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.
setUpFindPaint()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 
currentMatchBounds() const530 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 
currentMatchIsInLayer() const538 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 
currentMatchLayerId() const545 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.
storeCurrentMatchLocation()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 
draw(SkCanvas * canvas,LayerAndroid * layer,IntRect * inval)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.
drawMatch(const SkRegion & region,SkCanvas * canvas,bool focused)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 
findNext(bool forward)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.
setMatches(WTF::Vector<MatchInfo> * matches)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