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