1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2010 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include <algorithm>
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h"
11cb93a386Sopenharmony_ci#include "include/core/SkTime.h"
12cb93a386Sopenharmony_ci#include "tools/viewer/TouchGesture.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_cistatic const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500);
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_cistatic SkScalar pin_max_fling(SkScalar speed) {
19cb93a386Sopenharmony_ci    if (speed > MAX_FLING_SPEED) {
20cb93a386Sopenharmony_ci        speed = MAX_FLING_SPEED;
21cb93a386Sopenharmony_ci    }
22cb93a386Sopenharmony_ci    return speed;
23cb93a386Sopenharmony_ci}
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_cistatic double getseconds() {
26cb93a386Sopenharmony_ci    return SkTime::GetMSecs() * 0.001;
27cb93a386Sopenharmony_ci}
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci// returns +1 or -1, depending on the sign of x
30cb93a386Sopenharmony_ci// returns +1 if z is zero
31cb93a386Sopenharmony_cistatic SkScalar SkScalarSignNonZero(SkScalar x) {
32cb93a386Sopenharmony_ci    SkScalar sign = SK_Scalar1;
33cb93a386Sopenharmony_ci    if (x < 0) {
34cb93a386Sopenharmony_ci        sign = -sign;
35cb93a386Sopenharmony_ci    }
36cb93a386Sopenharmony_ci    return sign;
37cb93a386Sopenharmony_ci}
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_cistatic void unit_axis_align(SkVector* unit) {
40cb93a386Sopenharmony_ci    const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
41cb93a386Sopenharmony_ci    if (SkScalarAbs(unit->fX) < TOLERANCE) {
42cb93a386Sopenharmony_ci        unit->fX = 0;
43cb93a386Sopenharmony_ci        unit->fY = SkScalarSignNonZero(unit->fY);
44cb93a386Sopenharmony_ci    } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
45cb93a386Sopenharmony_ci        unit->fX = SkScalarSignNonZero(unit->fX);
46cb93a386Sopenharmony_ci        unit->fY = 0;
47cb93a386Sopenharmony_ci    }
48cb93a386Sopenharmony_ci}
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_civoid TouchGesture::FlingState::reset(float sx, float sy) {
51cb93a386Sopenharmony_ci    fActive = true;
52cb93a386Sopenharmony_ci    fDirection.set(sx, sy);
53cb93a386Sopenharmony_ci    fSpeed0 = SkPoint::Normalize(&fDirection);
54cb93a386Sopenharmony_ci    fSpeed0 = pin_max_fling(fSpeed0);
55cb93a386Sopenharmony_ci    fTime0 = getseconds();
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci    unit_axis_align(&fDirection);
58cb93a386Sopenharmony_ci//    printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
59cb93a386Sopenharmony_ci}
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_cibool TouchGesture::FlingState::evaluateMatrix(SkMatrix* matrix) {
62cb93a386Sopenharmony_ci    if (!fActive) {
63cb93a386Sopenharmony_ci        return false;
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    const float t =  (float)(getseconds() - fTime0);
67cb93a386Sopenharmony_ci    const float MIN_SPEED = 2;
68cb93a386Sopenharmony_ci    const float K0 = 5;
69cb93a386Sopenharmony_ci    const float K1 = 0.02f;
70cb93a386Sopenharmony_ci    const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
71cb93a386Sopenharmony_ci    if (speed <= MIN_SPEED) {
72cb93a386Sopenharmony_ci        fActive = false;
73cb93a386Sopenharmony_ci        return false;
74cb93a386Sopenharmony_ci    }
75cb93a386Sopenharmony_ci    float dist = (fSpeed0 - speed) / K0;
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_ci//    printf("---- time %g speed %g dist %g\n", t, speed, dist);
78cb93a386Sopenharmony_ci    float tx = fDirection.fX * dist;
79cb93a386Sopenharmony_ci    float ty = fDirection.fY * dist;
80cb93a386Sopenharmony_ci    if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
81cb93a386Sopenharmony_ci        tx = (float)sk_float_round2int(tx);
82cb93a386Sopenharmony_ci        ty = (float)sk_float_round2int(ty);
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci    matrix->setTranslate(tx, ty);
85cb93a386Sopenharmony_ci//    printf("---- evaluate (%g %g)\n", tx, ty);
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci    return true;
88cb93a386Sopenharmony_ci}
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_cistatic const SkMSec MAX_DBL_TAP_INTERVAL = 300;
93cb93a386Sopenharmony_cistatic const float MAX_DBL_TAP_DISTANCE = 100;
94cb93a386Sopenharmony_cistatic const float MAX_JITTER_RADIUS = 2;
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci// if true, then ignore the touch-move, 'cause its probably just jitter
97cb93a386Sopenharmony_cistatic bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
98cb93a386Sopenharmony_ci    return  sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
99cb93a386Sopenharmony_ci            sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
100cb93a386Sopenharmony_ci}
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ciTouchGesture::TouchGesture() {
105cb93a386Sopenharmony_ci    this->reset();
106cb93a386Sopenharmony_ci}
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ciTouchGesture::~TouchGesture() {
109cb93a386Sopenharmony_ci}
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_civoid TouchGesture::resetTouchState() {
112cb93a386Sopenharmony_ci    fIsTransLimited = false;
113cb93a386Sopenharmony_ci    fTouches.reset();
114cb93a386Sopenharmony_ci    fState = kEmpty_State;
115cb93a386Sopenharmony_ci    fLocalM.reset();
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci    fLastUpMillis = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
118cb93a386Sopenharmony_ci    fLastUpP.set(0, 0);
119cb93a386Sopenharmony_ci}
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_civoid TouchGesture::reset() {
122cb93a386Sopenharmony_ci    fGlobalM.reset();
123cb93a386Sopenharmony_ci    this->resetTouchState();
124cb93a386Sopenharmony_ci}
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_civoid TouchGesture::flushLocalM() {
127cb93a386Sopenharmony_ci    fGlobalM.postConcat(fLocalM);
128cb93a386Sopenharmony_ci    fLocalM.reset();
129cb93a386Sopenharmony_ci}
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ciconst SkMatrix& TouchGesture::localM() {
132cb93a386Sopenharmony_ci    if (fFlinger.isActive()) {
133cb93a386Sopenharmony_ci        if (!fFlinger.evaluateMatrix(&fLocalM)) {
134cb93a386Sopenharmony_ci            this->flushLocalM();
135cb93a386Sopenharmony_ci        }
136cb93a386Sopenharmony_ci    }
137cb93a386Sopenharmony_ci    return fLocalM;
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_civoid TouchGesture::appendNewRec(void* owner, float x, float y) {
141cb93a386Sopenharmony_ci    Rec* rec = fTouches.append();
142cb93a386Sopenharmony_ci    rec->fOwner = owner;
143cb93a386Sopenharmony_ci    rec->fStartX = rec->fPrevX = rec->fLastX = x;
144cb93a386Sopenharmony_ci    rec->fStartY = rec->fPrevY = rec->fLastY = y;
145cb93a386Sopenharmony_ci    rec->fLastT = rec->fPrevT = static_cast<float>(SkTime::GetSecs());
146cb93a386Sopenharmony_ci}
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_civoid TouchGesture::touchBegin(void* owner, float x, float y) {
149cb93a386Sopenharmony_ci//    SkDebugf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci    int index = this->findRec(owner);
152cb93a386Sopenharmony_ci    if (index >= 0) {
153cb93a386Sopenharmony_ci        this->flushLocalM();
154cb93a386Sopenharmony_ci        fTouches.removeShuffle(index);
155cb93a386Sopenharmony_ci        SkDebugf("---- already exists, removing\n");
156cb93a386Sopenharmony_ci    }
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci    if (fTouches.count() == 2) {
159cb93a386Sopenharmony_ci        return;
160cb93a386Sopenharmony_ci    }
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci    this->flushLocalM();
163cb93a386Sopenharmony_ci    fFlinger.stop();
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci    this->appendNewRec(owner, x, y);
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_ci    switch (fTouches.count()) {
168cb93a386Sopenharmony_ci        case 1:
169cb93a386Sopenharmony_ci            fState = kTranslate_State;
170cb93a386Sopenharmony_ci            break;
171cb93a386Sopenharmony_ci        case 2:
172cb93a386Sopenharmony_ci            this->startZoom();
173cb93a386Sopenharmony_ci            break;
174cb93a386Sopenharmony_ci        default:
175cb93a386Sopenharmony_ci            break;
176cb93a386Sopenharmony_ci    }
177cb93a386Sopenharmony_ci}
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ciint TouchGesture::findRec(void* owner) const {
180cb93a386Sopenharmony_ci    for (int i = 0; i < fTouches.count(); i++) {
181cb93a386Sopenharmony_ci        if (owner == fTouches[i].fOwner) {
182cb93a386Sopenharmony_ci            return i;
183cb93a386Sopenharmony_ci        }
184cb93a386Sopenharmony_ci    }
185cb93a386Sopenharmony_ci    return -1;
186cb93a386Sopenharmony_ci}
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_cistatic SkScalar center(float pos0, float pos1) {
189cb93a386Sopenharmony_ci    return (pos0 + pos1) * 0.5f;
190cb93a386Sopenharmony_ci}
191cb93a386Sopenharmony_ci
192cb93a386Sopenharmony_civoid TouchGesture::startZoom() {
193cb93a386Sopenharmony_ci    fState = kZoom_State;
194cb93a386Sopenharmony_ci}
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_civoid TouchGesture::updateZoom(float scale, float startX, float startY, float lastX, float lastY) {
197cb93a386Sopenharmony_ci    fLocalM.setTranslate(-startX, -startY);
198cb93a386Sopenharmony_ci    fLocalM.postScale(scale, scale);
199cb93a386Sopenharmony_ci    fLocalM.postTranslate(lastX, lastY);
200cb93a386Sopenharmony_ci}
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_civoid TouchGesture::endZoom() {
203cb93a386Sopenharmony_ci    this->flushLocalM();
204cb93a386Sopenharmony_ci    SkASSERT(kZoom_State == fState);
205cb93a386Sopenharmony_ci    fState = kEmpty_State;
206cb93a386Sopenharmony_ci}
207cb93a386Sopenharmony_ci
208cb93a386Sopenharmony_civoid TouchGesture::touchMoved(void* owner, float x, float y) {
209cb93a386Sopenharmony_ci//    SkDebugf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
210cb93a386Sopenharmony_ci
211cb93a386Sopenharmony_ci    if (kEmpty_State == fState) {
212cb93a386Sopenharmony_ci        return;
213cb93a386Sopenharmony_ci    }
214cb93a386Sopenharmony_ci
215cb93a386Sopenharmony_ci    int index = this->findRec(owner);
216cb93a386Sopenharmony_ci    if (index < 0) {
217cb93a386Sopenharmony_ci        SkDebugf("---- ignoring move without begin\n");
218cb93a386Sopenharmony_ci        return;
219cb93a386Sopenharmony_ci    }
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci    Rec& rec = fTouches[index];
222cb93a386Sopenharmony_ci
223cb93a386Sopenharmony_ci    // not sure how valuable this is
224cb93a386Sopenharmony_ci    if (fTouches.count() == 2) {
225cb93a386Sopenharmony_ci        if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
226cb93a386Sopenharmony_ci//            SkDebugf("--- drop touchMove, within jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
227cb93a386Sopenharmony_ci            return;
228cb93a386Sopenharmony_ci        }
229cb93a386Sopenharmony_ci    }
230cb93a386Sopenharmony_ci
231cb93a386Sopenharmony_ci    rec.fPrevX = rec.fLastX; rec.fLastX = x;
232cb93a386Sopenharmony_ci    rec.fPrevY = rec.fLastY; rec.fLastY = y;
233cb93a386Sopenharmony_ci    rec.fPrevT = rec.fLastT;
234cb93a386Sopenharmony_ci    rec.fLastT = static_cast<float>(SkTime::GetSecs());
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci    switch (fTouches.count()) {
237cb93a386Sopenharmony_ci        case 1: {
238cb93a386Sopenharmony_ci            float dx = rec.fLastX - rec.fStartX;
239cb93a386Sopenharmony_ci            float dy = rec.fLastY - rec.fStartY;
240cb93a386Sopenharmony_ci            dx = (float)sk_float_round2int(dx);
241cb93a386Sopenharmony_ci            dy = (float)sk_float_round2int(dy);
242cb93a386Sopenharmony_ci            fLocalM.setTranslate(dx, dy);
243cb93a386Sopenharmony_ci        } break;
244cb93a386Sopenharmony_ci        case 2: {
245cb93a386Sopenharmony_ci            SkASSERT(kZoom_State == fState);
246cb93a386Sopenharmony_ci            const Rec& rec0 = fTouches[0];
247cb93a386Sopenharmony_ci            const Rec& rec1 = fTouches[1];
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci            float scale = this->computePinch(rec0, rec1);
250cb93a386Sopenharmony_ci            this->updateZoom(scale,
251cb93a386Sopenharmony_ci                             center(rec0.fStartX, rec1.fStartX),
252cb93a386Sopenharmony_ci                             center(rec0.fStartY, rec1.fStartY),
253cb93a386Sopenharmony_ci                             center(rec0.fLastX, rec1.fLastX),
254cb93a386Sopenharmony_ci                             center(rec0.fLastY, rec1.fLastY));
255cb93a386Sopenharmony_ci        } break;
256cb93a386Sopenharmony_ci        default:
257cb93a386Sopenharmony_ci            break;
258cb93a386Sopenharmony_ci    }
259cb93a386Sopenharmony_ci}
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_civoid TouchGesture::touchEnd(void* owner) {
262cb93a386Sopenharmony_ci//    SkDebugf("--- %d touchEnd   %p\n", fTouches.count(), owner);
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ci    int index = this->findRec(owner);
265cb93a386Sopenharmony_ci    if (index < 0) {
266cb93a386Sopenharmony_ci        SkDebugf("--- not found\n");
267cb93a386Sopenharmony_ci        return;
268cb93a386Sopenharmony_ci    }
269cb93a386Sopenharmony_ci
270cb93a386Sopenharmony_ci    const Rec& rec = fTouches[index];
271cb93a386Sopenharmony_ci    if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
272cb93a386Sopenharmony_ci        return;
273cb93a386Sopenharmony_ci    }
274cb93a386Sopenharmony_ci
275cb93a386Sopenharmony_ci    // count() reflects the number before we removed the owner
276cb93a386Sopenharmony_ci    switch (fTouches.count()) {
277cb93a386Sopenharmony_ci        case 1: {
278cb93a386Sopenharmony_ci            this->flushLocalM();
279cb93a386Sopenharmony_ci            float dx = rec.fLastX - rec.fPrevX;
280cb93a386Sopenharmony_ci            float dy = rec.fLastY - rec.fPrevY;
281cb93a386Sopenharmony_ci            float dur = rec.fLastT - rec.fPrevT;
282cb93a386Sopenharmony_ci            if (dur > 0) {
283cb93a386Sopenharmony_ci                fFlinger.reset(dx / dur, dy / dur);
284cb93a386Sopenharmony_ci            }
285cb93a386Sopenharmony_ci            fState = kEmpty_State;
286cb93a386Sopenharmony_ci        } break;
287cb93a386Sopenharmony_ci        case 2:
288cb93a386Sopenharmony_ci            this->endZoom();
289cb93a386Sopenharmony_ci            break;
290cb93a386Sopenharmony_ci        default:
291cb93a386Sopenharmony_ci            SkASSERT(kZoom_State == fState);
292cb93a386Sopenharmony_ci            break;
293cb93a386Sopenharmony_ci    }
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci    fTouches.removeShuffle(index);
296cb93a386Sopenharmony_ci
297cb93a386Sopenharmony_ci    limitTrans();
298cb93a386Sopenharmony_ci}
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_cibool TouchGesture::isFling(SkPoint* dir) {
301cb93a386Sopenharmony_ci    if (fFlinger.isActive()) {
302cb93a386Sopenharmony_ci        SkScalar speed;
303cb93a386Sopenharmony_ci        fFlinger.get(dir, &speed);
304cb93a386Sopenharmony_ci        if (speed > 1000) {
305cb93a386Sopenharmony_ci            return true;
306cb93a386Sopenharmony_ci        }
307cb93a386Sopenharmony_ci    }
308cb93a386Sopenharmony_ci    return false;
309cb93a386Sopenharmony_ci}
310cb93a386Sopenharmony_ci
311cb93a386Sopenharmony_cifloat TouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
312cb93a386Sopenharmony_ci    double dx = rec0.fStartX - rec1.fStartX;
313cb93a386Sopenharmony_ci    double dy = rec0.fStartY - rec1.fStartY;
314cb93a386Sopenharmony_ci    double dist0 = sqrt(dx*dx + dy*dy);
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci    dx = rec0.fLastX - rec1.fLastX;
317cb93a386Sopenharmony_ci    dy = rec0.fLastY - rec1.fLastY;
318cb93a386Sopenharmony_ci    double dist1 = sqrt(dx*dx + dy*dy);
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_ci    double scale = dist1 / dist0;
321cb93a386Sopenharmony_ci    return (float)scale;
322cb93a386Sopenharmony_ci}
323cb93a386Sopenharmony_ci
324cb93a386Sopenharmony_cibool TouchGesture::handleDblTap(float x, float y) {
325cb93a386Sopenharmony_ci    bool found = false;
326cb93a386Sopenharmony_ci    double now = SkTime::GetMSecs();
327cb93a386Sopenharmony_ci    if (now - fLastUpMillis <= MAX_DBL_TAP_INTERVAL) {
328cb93a386Sopenharmony_ci        if (SkPoint::Length(fLastUpP.fX - x,
329cb93a386Sopenharmony_ci                            fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
330cb93a386Sopenharmony_ci            fFlinger.stop();
331cb93a386Sopenharmony_ci            fLocalM.reset();
332cb93a386Sopenharmony_ci            fGlobalM.reset();
333cb93a386Sopenharmony_ci            fTouches.reset();
334cb93a386Sopenharmony_ci            fState = kEmpty_State;
335cb93a386Sopenharmony_ci            found = true;
336cb93a386Sopenharmony_ci        }
337cb93a386Sopenharmony_ci    }
338cb93a386Sopenharmony_ci
339cb93a386Sopenharmony_ci    fLastUpMillis = now;
340cb93a386Sopenharmony_ci    fLastUpP.set(x, y);
341cb93a386Sopenharmony_ci    return found;
342cb93a386Sopenharmony_ci}
343cb93a386Sopenharmony_ci
344cb93a386Sopenharmony_civoid TouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect,
345cb93a386Sopenharmony_ci                                   const SkMatrix& preTouchMatrix) {
346cb93a386Sopenharmony_ci    fIsTransLimited = true;
347cb93a386Sopenharmony_ci    fContentRect = contentRect;
348cb93a386Sopenharmony_ci    fWindowRect = windowRect;
349cb93a386Sopenharmony_ci    fPreTouchM = preTouchMatrix;
350cb93a386Sopenharmony_ci}
351cb93a386Sopenharmony_ci
352cb93a386Sopenharmony_civoid TouchGesture::limitTrans() {
353cb93a386Sopenharmony_ci    if (!fIsTransLimited) {
354cb93a386Sopenharmony_ci        return;
355cb93a386Sopenharmony_ci    }
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ci    SkRect scaledContent = fContentRect;
358cb93a386Sopenharmony_ci    fPreTouchM.mapRect(&scaledContent);
359cb93a386Sopenharmony_ci    fGlobalM.mapRect(&scaledContent);
360cb93a386Sopenharmony_ci    const SkScalar ZERO = 0;
361cb93a386Sopenharmony_ci
362cb93a386Sopenharmony_ci    fGlobalM.postTranslate(ZERO, std::min(ZERO, fWindowRect.fBottom - scaledContent.fTop));
363cb93a386Sopenharmony_ci    fGlobalM.postTranslate(ZERO, std::max(ZERO, fWindowRect.fTop - scaledContent.fBottom));
364cb93a386Sopenharmony_ci    fGlobalM.postTranslate(std::min(ZERO, fWindowRect.fRight - scaledContent.fLeft), ZERO);
365cb93a386Sopenharmony_ci    fGlobalM.postTranslate(std::max(ZERO, fWindowRect.fLeft - scaledContent.fRight), ZERO);
366cb93a386Sopenharmony_ci}
367