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