Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
core
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
LibreOffice
core
Commits
2c15cb70
Kaydet (Commit)
2c15cb70
authored
Eyl 23, 2014
tarafından
Tomaž Vajngerl
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
android: extract touch handling into TouchEventHandler
Change-Id: I138f746940bf89349d4662c95427113bff221231
üst
b0a1d430
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
826 additions
and
298 deletions
+826
-298
DisplayPortCalculator.java
...src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
+433
-58
DisplayPortMetrics.java
...d3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
+0
-0
GeckoLayerClient.java
...oid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+16
-9
LayerController.java
...roid3/src/java/org/mozilla/gecko/gfx/LayerController.java
+34
-176
LayerView.java
.../LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java
+11
-49
TouchEventHandler.java
...id3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java
+283
-0
PanZoomController.java
...oid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
+49
-6
No files found.
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
Dosyayı görüntüle @
2c15cb70
...
...
@@ -5,97 +5,472 @@
package
org
.
mozilla
.
gecko
.
gfx
;
import
android.graphics.PointF
;
import
android.graphics.RectF
;
import
android.util.Log
;
import
org.libreoffice.LOKitShell
;
import
org.mozilla.gecko.util.FloatUtils
;
final
class
DisplayPortCalculator
{
private
static
final
String
LOGTAG
=
"GeckoDisplayPortCalculator"
;
private
static
final
PointF
ZERO_VELOCITY
=
new
PointF
(
0
,
0
);
private
static
final
int
DEFAULT_DISPLAY_PORT_MARGIN
=
300
;
private
static
DisplayPortStrategy
sStrategy
=
new
FixedMarginStrategy
()
;
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
* we start aggressively redrawing to minimize checkerboarding. */
private
static
final
int
DANGER_ZONE_X
=
75
;
private
static
final
int
DANGER_ZONE_Y
=
150
;
static
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
)
{
return
sStrategy
.
calculate
(
metrics
,
(
velocity
==
null
?
ZERO_VELOCITY
:
velocity
));
}
static
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
)
{
float
desiredXMargins
=
2
*
DEFAULT_DISPLAY_PORT_MARGIN
;
float
desiredYMargins
=
2
*
DEFAULT_DISPLAY_PORT_MARGIN
;
static
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
)
{
if
(
displayPort
==
null
)
{
return
true
;
}
return
sStrategy
.
aboutToCheckerboard
(
metrics
,
(
velocity
==
null
?
ZERO_VELOCITY
:
velocity
),
displayPort
);
}
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less.
/**
* Set the active strategy to use.
* See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the
* mapping between ints and strategies.
*/
static
void
setStrategy
(
int
strategy
)
{
switch
(
strategy
)
{
case
0
:
default
:
sStrategy
=
new
FixedMarginStrategy
();
break
;
case
1
:
sStrategy
=
new
VelocityBiasStrategy
();
break
;
case
2
:
sStrategy
=
new
DynamicResolutionStrategy
();
break
;
case
3
:
sStrategy
=
new
NoMarginStrategy
();
break
;
}
Log
.
i
(
LOGTAG
,
"Set strategy "
+
sStrategy
.
getClass
().
getName
());
}
private
interface
DisplayPortStrategy
{
/** Calculates a displayport given a viewport and panning velocity. */
public
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
);
/** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */
public
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
);
}
/**
* Return the dimensions for a rect that has area (width*height) that does not exceed the page size in the
* given metrics object. The area in the returned FloatSize may be less than width*height if the page is
* small, but it will never be larger than width*height.
* Note that this process may change the relative aspect ratio of the given dimensions.
*/
private
static
FloatSize
reshapeForPage
(
float
width
,
float
height
,
ImmutableViewportMetrics
metrics
)
{
// figure out how much of the desired buffer amount we can actually use on the horizontal axis
float
xBufferAmount
=
Math
.
min
(
desiredXMargins
,
metrics
.
pageSizeWidth
-
metrics
.
getWidth
()
);
float
usableWidth
=
Math
.
min
(
width
,
metrics
.
pageSizeWidth
);
// if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
// use it on the vertical axis
float
savedPixels
=
(
desiredXMargins
-
xBufferAmount
)
*
(
metrics
.
getHeight
()
+
desiredYMargins
);
float
extraYAmount
=
(
float
)
Math
.
floor
(
savedPixels
/
(
metrics
.
getWidth
()
+
xBufferAmount
)
);
float
yBufferAmount
=
Math
.
min
(
desiredYMargins
+
extraYAmount
,
metrics
.
pageSizeHeight
-
metrics
.
getHeight
());
float
extraUsableHeight
=
(
float
)
Math
.
floor
(((
width
-
usableWidth
)
*
height
)
/
usableWidth
);
float
usableHeight
=
Math
.
min
(
height
+
extraUsableHeight
,
metrics
.
pageSizeHeight
);
if
(
usableHeight
<
height
&&
usableWidth
==
width
)
{
// and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
if
(
xBufferAmount
==
desiredXMargins
&&
yBufferAmount
<
desiredYMargins
)
{
savedPixels
=
(
desiredYMargins
-
yBufferAmount
)
*
(
metrics
.
getWidth
()
+
xBufferAmount
);
float
extraXAmount
=
(
float
)
Math
.
floor
(
savedPixels
/
(
metrics
.
getHeight
()
+
yBufferAmount
));
xBufferAmount
=
Math
.
min
(
xBufferAmount
+
extraXAmount
,
metrics
.
pageSizeWidth
-
metrics
.
getWidth
());
float
extraUsableWidth
=
(
float
)
Math
.
floor
(((
height
-
usableHeight
)
*
width
)
/
usableHeight
);
usableWidth
=
Math
.
min
(
width
+
extraUsableWidth
,
metrics
.
pageSizeWidth
);
}
return
new
FloatSize
(
usableWidth
,
usableHeight
);
}
/**
* Expand the given rect in all directions by a "danger zone". The size of the danger zone on an axis
* is the size of the view on that axis multiplied by the given multiplier. The expanded rect is then
* clamped to page bounds and returned.
*/
private
static
RectF
expandByDangerZone
(
RectF
rect
,
float
dangerZoneXMultiplier
,
float
dangerZoneYMultiplier
,
ImmutableViewportMetrics
metrics
)
{
// calculate the danger zone amounts in pixels
float
dangerZoneX
=
metrics
.
getWidth
()
*
dangerZoneXMultiplier
;
float
dangerZoneY
=
metrics
.
getHeight
()
*
dangerZoneYMultiplier
;
rect
=
RectUtils
.
expand
(
rect
,
dangerZoneX
,
dangerZoneY
);
// clamp to page bounds
if
(
rect
.
top
<
0
)
rect
.
top
=
0
;
if
(
rect
.
left
<
0
)
rect
.
left
=
0
;
if
(
rect
.
right
>
metrics
.
pageSizeWidth
)
rect
.
right
=
metrics
.
pageSizeWidth
;
if
(
rect
.
bottom
>
metrics
.
pageSizeHeight
)
rect
.
bottom
=
metrics
.
pageSizeHeight
;
return
rect
;
}
/**
* Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
* does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
* it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
* metrics.pageSizeWidth; and the same for the y axis.
*/
private
static
RectF
shiftMarginsForPageBounds
(
RectF
margins
,
ImmutableViewportMetrics
metrics
)
{
// check how much we're overflowing in each direction. note that at most one of leftOverflow
// and rightOverflow can be greater than zero, and at most one of topOverflow and bottomOverflow
// can be greater than zero, because of the assumption described in the method javadoc.
float
leftOverflow
=
margins
.
left
-
metrics
.
viewportRectLeft
;
float
rightOverflow
=
margins
.
right
-
(
metrics
.
pageSizeWidth
-
metrics
.
viewportRectRight
);
float
topOverflow
=
margins
.
top
-
metrics
.
viewportRectTop
;
float
bottomOverflow
=
margins
.
bottom
-
(
metrics
.
pageSizeHeight
-
metrics
.
viewportRectBottom
);
// if the margins overflow the page bounds, shift them to other side on the same axis
if
(
leftOverflow
>
0
)
{
margins
.
left
-=
leftOverflow
;
margins
.
right
+=
leftOverflow
;
}
else
if
(
rightOverflow
>
0
)
{
margins
.
right
-=
rightOverflow
;
margins
.
left
+=
rightOverflow
;
}
if
(
topOverflow
>
0
)
{
margins
.
top
-=
topOverflow
;
margins
.
bottom
+=
topOverflow
;
}
else
if
(
bottomOverflow
>
0
)
{
margins
.
bottom
-=
bottomOverflow
;
margins
.
top
+=
bottomOverflow
;
}
return
margins
;
}
/**
* This class implements the variation where we basically don't bother with a a display port.
*/
private
static
class
NoMarginStrategy
implements
DisplayPortStrategy
{
public
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
)
{
return
new
DisplayPortMetrics
(
metrics
.
viewportRectLeft
,
metrics
.
viewportRectTop
,
metrics
.
viewportRectRight
,
metrics
.
viewportRectBottom
,
metrics
.
zoomFactor
);
}
public
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
)
{
return
true
;
}
}
/**
* This class implements the variation where we use a fixed-size margin on the display port.
* The margin is always 300 pixels in all directions, except when we are (a) approaching a page
* boundary, and/or (b) if we are limited by the page size. In these cases we try to maintain
* the area of the display port by (a) shifting the buffer to the other side on the same axis,
* and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on
* one axis.
*/
private
static
class
FixedMarginStrategy
implements
DisplayPortStrategy
{
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private
static
final
float
SIZE_MULTIPLIER
=
1.5f
;
// If the visible rect is within the danger zone (measured as a fraction of the view size
// from the edge of the displayport) we start redrawing to minimize checkerboarding.
private
static
final
float
DANGER_ZONE_X_MULTIPLIER
=
0.10f
;
private
static
final
float
DANGER_ZONE_Y_MULTIPLIER
=
0.20f
;
public
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
)
{
float
displayPortWidth
=
metrics
.
getWidth
()
*
SIZE_MULTIPLIER
;
float
displayPortHeight
=
metrics
.
getHeight
()
*
SIZE_MULTIPLIER
;
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less. reshape the display
// port dimensions to accomplish this.
FloatSize
usableSize
=
reshapeForPage
(
displayPortWidth
,
displayPortHeight
,
metrics
);
float
horizontalBuffer
=
usableSize
.
width
-
metrics
.
getWidth
();
float
verticalBuffer
=
usableSize
.
height
-
metrics
.
getHeight
();
// and now calculate the display port margins based on how much buffer we've decided to use and
// the page bounds, ensuring we use all of the available buffer amounts on one side or the other
// on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
// entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
// is split).
float
leftMargin
=
Math
.
min
(
DEFAULT_DISPLAY_PORT_MARGIN
,
metrics
.
viewportRectLeft
);
float
rightMargin
=
Math
.
min
(
DEFAULT_DISPLAY_PORT_MARGIN
,
metrics
.
pageSizeWidth
-
(
metrics
.
viewportRectLeft
+
metrics
.
getWidth
()));
if
(
leftMargin
<
DEFAULT_DISPLAY_PORT_MARGIN
)
{
rightMargin
=
xBufferAmount
-
leftMargin
;
}
else
if
(
rightMargin
<
DEFAULT_DISPLAY_PORT_MARGIN
)
{
leftMargin
=
xBufferAmount
-
rightMargin
;
}
else
if
(!
FloatUtils
.
fuzzyEquals
(
leftMargin
+
rightMargin
,
xBufferAmount
))
{
float
delta
=
xBufferAmount
-
leftMargin
-
rightMargin
;
leftMargin
+=
delta
/
2
;
rightMargin
+=
delta
/
2
;
}
float
topMargin
=
Math
.
min
(
DEFAULT_DISPLAY_PORT_MARGIN
,
metrics
.
viewportRectTop
);
float
bottomMargin
=
Math
.
min
(
DEFAULT_DISPLAY_PORT_MARGIN
,
metrics
.
pageSizeHeight
-
(
metrics
.
viewportRectTop
+
metrics
.
getHeight
()));
if
(
topMargin
<
DEFAULT_DISPLAY_PORT_MARGIN
)
{
bottomMargin
=
yBufferAmount
-
topMargin
;
}
else
if
(
bottomMargin
<
DEFAULT_DISPLAY_PORT_MARGIN
)
{
topMargin
=
yBufferAmount
-
bottomMargin
;
}
else
if
(!
FloatUtils
.
fuzzyEquals
(
topMargin
+
bottomMargin
,
yBufferAmount
))
{
float
delta
=
yBufferAmount
-
topMargin
-
bottomMargin
;
topMargin
+=
delta
/
2
;
bottomMargin
+=
delta
/
2
;
}
RectF
margins
=
new
RectF
();
margins
.
left
=
horizontalBuffer
/
2.0f
;
margins
.
right
=
horizontalBuffer
-
margins
.
left
;
margins
.
top
=
verticalBuffer
/
2.0f
;
margins
.
bottom
=
verticalBuffer
-
margins
.
top
;
margins
=
shiftMarginsForPageBounds
(
margins
,
metrics
);
// note that unless the viewport size changes, or the page dimensions change (either because of
// content changes or zooming), the size of the display port should remain constant. this
// is intentional to avoid re-creating textures and all sorts of other reallocations in the
// draw and composition code.
return
new
DisplayPortMetrics
(
metrics
.
viewportRectLeft
-
leftMargin
,
metrics
.
viewportRectTop
-
topMargin
,
metrics
.
viewportRectRight
+
rightMargin
,
metrics
.
viewportRectBottom
+
bottomMargin
,
return
new
DisplayPortMetrics
(
metrics
.
viewportRectLeft
-
margins
.
left
,
metrics
.
viewportRectTop
-
margins
.
top
,
metrics
.
viewportRectRight
+
margins
.
right
,
metrics
.
viewportRectBottom
+
margins
.
bottom
,
metrics
.
zoomFactor
);
}
// Returns true if a checkerboard is about to be visible.
static
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
DisplayPortMetrics
displayPort
)
{
if
(
displayPort
==
null
)
{
public
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
)
{
// Increase the size of the viewport based on the danger zone multiplier (and clamp to page
// boundaries), and intersect it with the current displayport to determine whether we're
// close to checkerboarding.
RectF
adjustedViewport
=
expandByDangerZone
(
metrics
.
getViewport
(),
DANGER_ZONE_X_MULTIPLIER
,
DANGER_ZONE_Y_MULTIPLIER
,
metrics
);
return
!
displayPort
.
contains
(
adjustedViewport
);
}
}
/**
* This class implements the variation with a small fixed-size margin with velocity bias.
* In this variation, the default margins are pretty small relative to the view size, but
* they are affected by the panning velocity. Specifically, if we are panning on one axis,
* we remove the margins on the other axis because we are likely axis-locked. Also once
* we are panning in one direction above a certain threshold velocity, we shift the buffer
* so that it is entirely in the direction of the pan.
*/
private
static
class
VelocityBiasStrategy
implements
DisplayPortStrategy
{
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private
static
final
float
SIZE_MULTIPLIER
=
1.2f
;
// The velocity above which we apply the velocity bias
private
static
final
float
VELOCITY_THRESHOLD
=
LOKitShell
.
getDpi
()
/
32
f
;
public
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
)
{
float
displayPortWidth
=
metrics
.
getWidth
()
*
SIZE_MULTIPLIER
;
float
displayPortHeight
=
metrics
.
getHeight
()
*
SIZE_MULTIPLIER
;
// but if we're panning on one axis, set the margins for the other axis to zero since we are likely
// axis locked and won't be displaying that extra area.
if
(
Math
.
abs
(
velocity
.
x
)
>
VELOCITY_THRESHOLD
&&
FloatUtils
.
fuzzyEquals
(
velocity
.
y
,
0
))
{
displayPortHeight
=
metrics
.
getHeight
();
}
else
if
(
Math
.
abs
(
velocity
.
y
)
>
VELOCITY_THRESHOLD
&&
FloatUtils
.
fuzzyEquals
(
velocity
.
x
,
0
))
{
displayPortWidth
=
metrics
.
getWidth
();
}
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169).
displayPortWidth
=
Math
.
min
(
displayPortWidth
,
metrics
.
pageSizeWidth
);
displayPortHeight
=
Math
.
min
(
displayPortHeight
,
metrics
.
pageSizeHeight
);
float
horizontalBuffer
=
displayPortWidth
-
metrics
.
getWidth
();
float
verticalBuffer
=
displayPortHeight
-
metrics
.
getHeight
();
// if we're panning above the VELOCITY_THRESHOLD on an axis, apply the margin so that it
// is entirely in the direction of panning. Otherwise, split the margin evenly on both sides of
// the display port.
RectF
margins
=
new
RectF
();
if
(
velocity
.
x
>
VELOCITY_THRESHOLD
)
{
margins
.
right
=
horizontalBuffer
;
}
else
if
(
velocity
.
x
<
-
VELOCITY_THRESHOLD
)
{
margins
.
left
=
horizontalBuffer
;
}
else
{
margins
.
left
=
horizontalBuffer
/
2.0f
;
margins
.
right
=
horizontalBuffer
-
margins
.
left
;
}
if
(
velocity
.
y
>
VELOCITY_THRESHOLD
)
{
margins
.
bottom
=
verticalBuffer
;
}
else
if
(
velocity
.
y
<
-
VELOCITY_THRESHOLD
)
{
margins
.
top
=
verticalBuffer
;
}
else
{
margins
.
top
=
verticalBuffer
/
2.0f
;
margins
.
bottom
=
verticalBuffer
-
margins
.
top
;
}
// and finally shift the margins to account for page bounds
margins
=
shiftMarginsForPageBounds
(
margins
,
metrics
);
return
new
DisplayPortMetrics
(
metrics
.
viewportRectLeft
-
margins
.
left
,
metrics
.
viewportRectTop
-
margins
.
top
,
metrics
.
viewportRectRight
+
margins
.
right
,
metrics
.
viewportRectBottom
+
margins
.
bottom
,
metrics
.
zoomFactor
);
}
public
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
)
{
// Since we have such a small margin, we want to be drawing more aggressively. At the start of a
// pan the velocity is going to be large so we're almost certainly going to go into checkerboard
// on every frame, so drawing all the time seems like the right thing. At the end of the pan we
// want to re-center the displayport and draw stuff on all sides, so again we don't want to throttle
// there. When we're not panning we're not drawing anyway so it doesn't make a difference there.
return
true
;
}
}
/**
* This class implements the variation where we draw more of the page at low resolution while panning.
* In this variation, as we pan faster, we increase the page area we are drawing, but reduce the draw
* resolution to compensate. This results in the same device-pixel area drawn; the compositor then
* scales this up to the viewport zoom level. This results in a large area of the page drawn but it
* looks blurry. The assumption is that drawing extra that we never display is better than checkerboarding,
* where we draw less but never even show it on the screen.
*/
private
static
class
DynamicResolutionStrategy
implements
DisplayPortStrategy
{
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private
static
final
float
SIZE_MULTIPLIER
=
1.5f
;
// The velocity above which we start zooming out the display port to keep up
// with the panning.
private
static
final
float
VELOCITY_EXPANSION_THRESHOLD
=
LOKitShell
.
getDpi
()
/
16
f
;
// How much we increase the display port based on velocity. Assuming no friction and
// splitting (see below), this should be be the number of frames (@60fps) between us
// calculating the display port and the draw of the *next* display port getting composited
// and displayed on the screen. This is because the timeline looks like this:
// Java: pan pan pan pan pan pan ! pan pan pan pan pan pan !
// Gecko: \-> draw -> composite / \-> draw -> composite /
// The display port calculated on the first "pan" gets composited to the screen at the
// first exclamation mark, and remains on the screen until the second exclamation mark.
// In order to avoid checkerboarding, that display port must be able to contain all of
// the panning until the second exclamation mark, which encompasses two entire draw/composite
// cycles.
// If we take into account friction, our velocity multiplier should be reduced as the
// amount of pan will decrease each time. If we take into account display port splitting,
// it should be increased as the splitting means some of the display port will be used to
// draw in the opposite direction of the velocity. For now I'm assuming these two cancel
// each other out.
private
static
final
float
VELOCITY_MULTIPLIER
=
60.0f
;
// The following constants adjust how biased the display port is in the direction of panning.
// When panning fast (above the FAST_THRESHOLD) we use the fast split factor to split the
// display port "buffer" area, otherwise we use the slow split factor. This is based on the
// assumption that if the user is panning fast, they are less likely to reverse directions
// and go backwards, so we should spend more of our display port buffer in the direction of
// panning.
private
static
final
float
VELOCITY_FAST_THRESHOLD
=
VELOCITY_EXPANSION_THRESHOLD
*
2.0f
;
private
static
final
float
FAST_SPLIT_FACTOR
=
0.95f
;
private
static
final
float
SLOW_SPLIT_FACTOR
=
0.8f
;
// The following constants are used for viewport prediction; we use them to estimate where
// the viewport will be soon and whether or not we should trigger a draw right now. "soon"
// in the previous sentence really refers to the amount of time it would take to draw and
// composite from the point at which we do the calculation, and that is not really a known
// quantity. The velocity multiplier is how much we multiply the velocity by; it has the
// same caveats as the VELOCITY_MULTIPLIER above except that it only needs to take into account
// one draw/composite cycle instead of two. The danger zone multiplier is a multiplier of the
// viewport size that we use as an extra "danger zone" around the viewport; if this danger
// zone falls outside the display port then we are approaching the point at which we will
// checkerboard, and hence should start drawing. Note that if DANGER_ZONE_MULTIPLIER is
// greater than (SIZE_MULTIPLIER - 1.0f), then at zero velocity we will always be in the
// danger zone, and thus will be constantly drawing.
private
static
final
float
PREDICTION_VELOCITY_MULTIPLIER
=
30.0f
;
private
static
final
float
DANGER_ZONE_MULTIPLIER
=
0.20f
;
// must be less than (SIZE_MULTIPLIER - 1.0f)
public
DisplayPortMetrics
calculate
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
)
{
float
displayPortWidth
=
metrics
.
getWidth
()
*
SIZE_MULTIPLIER
;
float
displayPortHeight
=
metrics
.
getHeight
()
*
SIZE_MULTIPLIER
;
// for resolution calculation purposes, we need to know what the adjusted display port dimensions
// would be if we had zero velocity, so calculate that here before we increase the display port
// based on velocity.
FloatSize
reshapedSize
=
reshapeForPage
(
displayPortWidth
,
displayPortHeight
,
metrics
);
// increase displayPortWidth and displayPortHeight based on the velocity, but maintaining their
// relative aspect ratio.
if
(
velocity
.
length
()
>
VELOCITY_EXPANSION_THRESHOLD
)
{
float
velocityFactor
=
Math
.
max
(
Math
.
abs
(
velocity
.
x
)
/
displayPortWidth
,
Math
.
abs
(
velocity
.
y
)
/
displayPortHeight
);
velocityFactor
*=
VELOCITY_MULTIPLIER
;
displayPortWidth
+=
(
displayPortWidth
*
velocityFactor
);
displayPortHeight
+=
(
displayPortHeight
*
velocityFactor
);
}
// Increase the size of the viewport (and clamp to page boundaries), and
// intersect it with the tile's displayport to determine whether we're
// at this point, displayPortWidth and displayPortHeight are how much of the page (in device pixels)
// we want to be rendered by Gecko. Note here "device pixels" is equivalent to CSS pixels multiplied
// by metrics.zoomFactor
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less. reshape the display
// port dimensions to accomplish this. this may change the aspect ratio of the display port,
// but we are assuming that this is desirable because the advantages from pre-drawing will
// outweigh the disadvantages from any buffer reallocations that might occur.
FloatSize
usableSize
=
reshapeForPage
(
displayPortWidth
,
displayPortHeight
,
metrics
);
float
horizontalBuffer
=
usableSize
.
width
-
metrics
.
getWidth
();
float
verticalBuffer
=
usableSize
.
height
-
metrics
.
getHeight
();
// at this point, horizontalBuffer and verticalBuffer are the dimensions of the buffer area we have.
// the buffer area is the off-screen area that is part of the display port and will be pre-drawn in case
// the user scrolls there. we now need to split the buffer area on each axis so that we know
// what the exact margins on each side will be. first we split the buffer amount based on the direction
// we're moving, so that we have a larger buffer in the direction of travel.
RectF
margins
=
new
RectF
();
margins
.
left
=
splitBufferByVelocity
(
horizontalBuffer
,
velocity
.
x
);
margins
.
right
=
horizontalBuffer
-
margins
.
left
;
margins
.
top
=
splitBufferByVelocity
(
verticalBuffer
,
velocity
.
y
);
margins
.
bottom
=
verticalBuffer
-
margins
.
top
;
// then, we account for running into the page bounds - so that if we hit the top of the page, we need
// to drop the top margin and move that amount to the bottom margin.
margins
=
shiftMarginsForPageBounds
(
margins
,
metrics
);
// finally, we calculate the resolution we want to render the display port area at. We do this
// so that as we expand the display port area (because of velocity), we reduce the resolution of
// the painted area so as to maintain the size of the buffer Gecko is painting into. we calculate
// the reduction in resolution by comparing the display port size with and without the velocity
// changes applied.
// this effectively means that as we pan faster and faster, the display port grows, but we paint
// at lower resolutions. this paints more area to reduce checkerboard at the cost of increasing
// compositor-scaling and blurriness. Once we stop panning, the blurriness must be entirely gone.
// Note that usable* could be less than base* if we are pinch-zoomed out into overscroll, so we
// clamp it to make sure this doesn't increase our display resolution past metrics.zoomFactor.
float
scaleFactor
=
Math
.
min
(
reshapedSize
.
width
/
usableSize
.
width
,
reshapedSize
.
height
/
usableSize
.
height
);
float
displayResolution
=
metrics
.
zoomFactor
*
Math
.
min
(
1.0f
,
scaleFactor
);
DisplayPortMetrics
dpMetrics
=
new
DisplayPortMetrics
(
metrics
.
viewportRectLeft
-
margins
.
left
,
metrics
.
viewportRectTop
-
margins
.
top
,
metrics
.
viewportRectRight
+
margins
.
right
,
metrics
.
viewportRectBottom
+
margins
.
bottom
,
displayResolution
);
return
dpMetrics
;
}
/**
* Split the given buffer amount into two based on the velocity.
* Given an amount of total usable buffer on an axis, this will
* return the amount that should be used on the left/top side of
* the axis (the side which a negative velocity vector corresponds
* to).
*/
private
float
splitBufferByVelocity
(
float
amount
,
float
velocity
)
{
// if no velocity, so split evenly
if
(
FloatUtils
.
fuzzyEquals
(
velocity
,
0
))
{
return
amount
/
2.0f
;
}
// if we're moving quickly, assign more of the amount in that direction
// since is is less likely that we will reverse direction immediately
if
(
velocity
<
-
VELOCITY_FAST_THRESHOLD
)
{
return
amount
*
FAST_SPLIT_FACTOR
;
}
if
(
velocity
>
VELOCITY_FAST_THRESHOLD
)
{
return
amount
*
(
1.0f
-
FAST_SPLIT_FACTOR
);
}
// if we're moving slowly, then assign less of the amount in that direction
if
(
velocity
<
0
)
{
return
amount
*
SLOW_SPLIT_FACTOR
;
}
else
{
return
amount
*
(
1.0f
-
SLOW_SPLIT_FACTOR
);
}
}
public
boolean
aboutToCheckerboard
(
ImmutableViewportMetrics
metrics
,
PointF
velocity
,
DisplayPortMetrics
displayPort
)
{
// Expand the viewport based on our velocity (and clamp it to page boundaries).
// Then intersect it with the last-requested displayport to determine whether we're
// close to checkerboarding.
FloatSize
pageSize
=
metrics
.
getPageSize
();
RectF
adjustedViewport
=
RectUtils
.
expand
(
metrics
.
getViewport
(),
DANGER_ZONE_X
,
DANGER_ZONE_Y
);
if
(
adjustedViewport
.
top
<
0
)
adjustedViewport
.
top
=
0
;
if
(
adjustedViewport
.
left
<
0
)
adjustedViewport
.
left
=
0
;
if
(
adjustedViewport
.
right
>
pageSize
.
width
)
adjustedViewport
.
right
=
pageSize
.
width
;
if
(
adjustedViewport
.
bottom
>
pageSize
.
height
)
adjustedViewport
.
bottom
=
pageSize
.
height
;
return
!
displayPort
.
contains
(
adjustedViewport
);
RectF
predictedViewport
=
metrics
.
getViewport
();
// first we expand the viewport in the direction we're moving based on some
// multiple of the current velocity.
if
(
velocity
.
length
()
>
0
)
{
if
(
velocity
.
x
<
0
)
{
predictedViewport
.
left
+=
velocity
.
x
*
PREDICTION_VELOCITY_MULTIPLIER
;
}
else
if
(
velocity
.
x
>
0
)
{
predictedViewport
.
right
+=
velocity
.
x
*
PREDICTION_VELOCITY_MULTIPLIER
;
}
if
(
velocity
.
y
<
0
)
{
predictedViewport
.
top
+=
velocity
.
y
*
PREDICTION_VELOCITY_MULTIPLIER
;
}
else
if
(
velocity
.
y
>
0
)
{
predictedViewport
.
bottom
+=
velocity
.
y
*
PREDICTION_VELOCITY_MULTIPLIER
;
}
}
// then we expand the viewport evenly in all directions just to have an extra
// safety zone. this also clamps it to page bounds.
predictedViewport
=
expandByDangerZone
(
predictedViewport
,
DANGER_ZONE_MULTIPLIER
,
DANGER_ZONE_MULTIPLIER
,
metrics
);
return
!
displayPort
.
contains
(
predictedViewport
);
}
}
}
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
Dosyayı görüntüle @
2c15cb70
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
Dosyayı görüntüle @
2c15cb70
...
...
@@ -212,7 +212,7 @@ public class GeckoLayerClient {
mLayerController
.
getView
().
postDelayed
(
new
AdjustRunnable
(),
MIN_VIEWPORT_CHANGE_DELAY
-
timeDelta
);
mPendingViewportAdjust
=
true
;
}
else
{
adjustViewport
();
adjustViewport
(
null
);
}
}
...
...
@@ -220,15 +220,21 @@ public class GeckoLayerClient {
mViewportSizeChanged
=
true
;
}
private
void
adjustViewport
()
{
ViewportMetrics
viewportMetrics
=
new
ViewportMetrics
(
mLayerController
.
getViewportMetrics
());
void
adjustViewport
(
DisplayPortMetrics
displayPort
)
{
ImmutableViewportMetrics
metrics
=
mLayerController
.
getViewportMetrics
();
viewportMetrics
.
setViewport
(
viewportMetrics
.
getClampedViewport
());
ViewportMetrics
clampedMetrics
=
new
ViewportMetrics
(
metrics
);
clampedMetrics
.
setViewport
(
clampedMetrics
.
getClampedViewport
());
mDisplayPort
=
DisplayPortCalculator
.
calculate
(
mLayerController
.
getViewportMetrics
());
if
(
displayPort
==
null
)
{
displayPort
=
DisplayPortCalculator
.
calculate
(
metrics
,
mLayerController
.
getPanZoomController
().
getVelocityVector
());
}
mDisplayPort
=
displayPort
;
mGeckoViewport
=
clampedMetrics
;
LOKitShell
.
sendEvent
(
LOEvent
.
viewport
(
viewport
Metrics
));
LOKitShell
.
sendEvent
(
LOEvent
.
viewport
(
clamped
Metrics
));
if
(
mViewportSizeChanged
)
{
mViewportSizeChanged
=
false
;
LOKitShell
.
viewSizeChanged
();
...
...
@@ -240,7 +246,7 @@ public class GeckoLayerClient {
public
void
geometryChanged
()
{
sendResizeEventIfNecessary
(
false
);
if
(
mLayerController
.
getRedrawHint
())
adjustViewport
();
adjustViewport
(
null
);
}
public
ViewportMetrics
getGeckoViewportMetrics
()
{
...
...
@@ -267,7 +273,7 @@ public class GeckoLayerClient {
private
class
AdjustRunnable
implements
Runnable
{
public
void
run
()
{
mPendingViewportAdjust
=
false
;
adjustViewport
();
adjustViewport
(
null
);
}
}
}
\ No newline at end of file
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
Dosyayı görüntüle @
2c15cb70
...
...
@@ -43,18 +43,12 @@ import android.content.res.Resources;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.graphics.PointF
;
import
android.graphics.Rect
;
import
android.graphics.RectF
;
import
android.util.Log
;
import
android.view.GestureDetector
;
import
android.view.MotionEvent
;
import
android.view.View.OnTouchListener
;
import
org.mozilla.gecko.ui.PanZoomController
;
import
org.mozilla.gecko.ui.SimpleScaleGestureDetector
;
import
java.util.Timer
;
import
java.util.TimerTask
;
import
java.util.regex.Pattern
;
/**
...
...
@@ -84,42 +78,20 @@ public class LayerController {
* fields. */
private
volatile
ImmutableViewportMetrics
mViewportMetrics
;
/* The current viewport metrics. */
private
boolean
mWaitForTouchListeners
;
private
PanZoomController
mPanZoomController
;
/*
* The panning and zooming controller, which interprets pan and zoom gestures for us and
* updates our visible rect appropriately.
*/
private
PanZoomController
mPanZoomController
;
private
OnTouchListener
mOnTouchListener
;
/* The touch listener. */
private
GeckoLayerClient
mLayerClient
;
/* The layer client. */
/* The new color for the checkerboard. */
private
int
mCheckerboardColor
;
private
boolean
mCheckerboardShouldShowChecks
=
true
;
private
boolean
mCheckerboardShouldShowChecks
;
private
boolean
mForceRedraw
;
/* The extra area on the sides of the page that we want to buffer to help with
* smooth, asynchronous scrolling. Depending on a device's support for NPOT
* textures, this may be rounded up to the nearest power of two.
*/
public
static
final
IntSize
MIN_BUFFER
=
new
IntSize
(
512
,
1024
);
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
* we start aggressively redrawing to minimize checkerboarding. */
private
static
final
int
DANGER_ZONE_X
=
75
;
private
static
final
int
DANGER_ZONE_Y
=
150
;
/* The time limit for pages to respond with preventDefault on touchevents
* before we begin panning the page */
private
int
mTimeout
=
200
;
private
boolean
allowDefaultActions
=
true
;
private
Timer
allowDefaultTimer
=
null
;
private
PointF
initialTouchLocation
=
null
;
private
static
Pattern
sColorPattern
;
public
LayerController
(
Context
context
)
{
...
...
@@ -129,9 +101,7 @@ public class LayerController {
mViewportMetrics
=
new
ImmutableViewportMetrics
(
new
ViewportMetrics
());
mPanZoomController
=
new
PanZoomController
(
this
);
mView
=
new
LayerView
(
context
,
this
);
}
public
void
onDestroy
()
{
mCheckerboardShouldShowChecks
=
true
;
}
public
void
setRoot
(
Layer
layer
)
{
mRootLayer
=
layer
;
}
...
...
@@ -197,50 +167,13 @@ public class LayerController {
* result in an infinite loop.
*/
public
void
setViewportSize
(
FloatSize
size
)
{
// Resize the viewport, and modify its zoom factor so that the page retains proportionally
// zoomed relative to the screen.
ViewportMetrics
viewportMetrics
=
new
ViewportMetrics
(
mViewportMetrics
);
float
oldHeight
=
viewportMetrics
.
getSize
().
height
;
float
oldWidth
=
viewportMetrics
.
getSize
().
width
;
float
oldZoomFactor
=
viewportMetrics
.
getZoomFactor
();
viewportMetrics
.
setSize
(
size
);
// if the viewport got larger (presumably because the vkb went away), and the page
// is smaller than the new viewport size, increase the page size so that the panzoomcontroller
// doesn't zoom in to make it fit (bug 718270). this page size change is in anticipation of
// gecko increasing the page size to match the new viewport size, which will happen the next
// time we get a draw update.
if
(
size
.
width
>=
oldWidth
&&
size
.
height
>=
oldHeight
)
{
FloatSize
pageSize
=
viewportMetrics
.
getPageSize
();
if
(
pageSize
.
width
<
size
.
width
||
pageSize
.
height
<
size
.
height
)
{
FloatSize
actualPageSize
=
new
FloatSize
(
Math
.
max
(
pageSize
.
width
,
size
.
width
),
Math
.
max
(
pageSize
.
height
,
size
.
height
));
viewportMetrics
.
setPageSize
(
actualPageSize
,
actualPageSize
);
}
}
// For rotations, we want the focus point to be at the top left.
boolean
rotation
=
(
size
.
width
>
oldWidth
&&
size
.
height
<
oldHeight
)
||
(
size
.
width
<
oldWidth
&&
size
.
height
>
oldHeight
);
PointF
newFocus
;
if
(
rotation
)
{
newFocus
=
new
PointF
(
0
,
0
);
}
else
{
newFocus
=
new
PointF
(
size
.
width
/
2.0f
,
size
.
height
/
2.0f
);
}
float
newZoomFactor
=
size
.
width
*
oldZoomFactor
/
oldWidth
;
viewportMetrics
.
scaleTo
(
newZoomFactor
,
newFocus
);
mViewportMetrics
=
new
ImmutableViewportMetrics
(
viewportMetrics
);
setForceRedraw
();
if
(
mLayerClient
!=
null
)
{
mLayerClient
.
viewportSizeChanged
();
notifyLayerClientOfGeometryChange
();
}
mPanZoomController
.
abortAnimation
();
mView
.
requestRender
();
}
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
...
...
@@ -283,10 +216,21 @@ public class LayerController {
*/
public
void
setViewportMetrics
(
ViewportMetrics
viewport
)
{
mViewportMetrics
=
new
ImmutableViewportMetrics
(
viewport
);
Log
.
d
(
LOGTAG
,
"setViewportMetrics: "
+
mViewportMetrics
);
mView
.
requestRender
();
}
public
void
setAnimationTarget
(
ViewportMetrics
viewport
)
{
if
(
mLayerClient
!=
null
)
{
// We know what the final viewport of the animation is going to be, so
// immediately request a draw of that area by setting the display port
// accordingly. This way we should have the content pre-rendered by the
// time the animation is done.
ImmutableViewportMetrics
metrics
=
new
ImmutableViewportMetrics
(
viewport
);
DisplayPortMetrics
displayPort
=
DisplayPortCalculator
.
calculate
(
metrics
,
null
);
mLayerClient
.
adjustViewport
(
displayPort
);
}
}
/**
* Scales the viewport, keeping the given focus point in the same place before and after the
* scale operation. You must hold the monitor while calling this.
...
...
@@ -295,7 +239,6 @@ public class LayerController {
ViewportMetrics
viewportMetrics
=
new
ViewportMetrics
(
mViewportMetrics
);
viewportMetrics
.
scaleTo
(
zoomFactor
,
focus
);
mViewportMetrics
=
new
ImmutableViewportMetrics
(
viewportMetrics
);
Log
.
d
(
LOGTAG
,
"scaleWithFocus: "
+
mViewportMetrics
+
"; zf="
+
zoomFactor
);
// We assume the zoom level will only be modified by the
// PanZoomController, so no need to notify it of this change.
...
...
@@ -305,10 +248,6 @@ public class LayerController {
public
boolean
post
(
Runnable
action
)
{
return
mView
.
post
(
action
);
}
public
void
setOnTouchListener
(
OnTouchListener
onTouchListener
)
{
mOnTouchListener
=
onTouchListener
;
}
/**
* The view as well as the controller itself use this method to notify the layer client that
* the geometry changed.
...
...
@@ -343,119 +282,38 @@ public class LayerController {
return
false
;
}
return
aboutToCheckerboard
();
}
// Returns true if a checkerboard is about to be visible.
private
boolean
aboutToCheckerboard
()
{
// Increase the size of the viewport (and clamp to page boundaries), and
// intersect it with the tile's displayport to determine whether we're
// close to checkerboarding.
FloatSize
pageSize
=
getPageSize
();
RectF
adjustedViewport
=
RectUtils
.
expand
(
getViewport
(),
DANGER_ZONE_X
,
DANGER_ZONE_Y
);
if
(
adjustedViewport
.
top
<
0
)
adjustedViewport
.
top
=
0
;
if
(
adjustedViewport
.
left
<
0
)
adjustedViewport
.
left
=
0
;
if
(
adjustedViewport
.
right
>
pageSize
.
width
)
adjustedViewport
.
right
=
pageSize
.
width
;
if
(
adjustedViewport
.
bottom
>
pageSize
.
height
)
adjustedViewport
.
bottom
=
pageSize
.
height
;
DisplayPortMetrics
displayPort
=
(
mLayerClient
==
null
?
new
DisplayPortMetrics
()
:
mLayerClient
.
getDisplayPort
());
return
!
displayPort
.
contains
(
adjustedViewport
);
return
DisplayPortCalculator
.
aboutToCheckerboard
(
mViewportMetrics
,
mPanZoomController
.
getVelocityVector
(),
mLayerClient
.
getDisplayPort
());
}
/**
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
* point measured in pixels from the top left corner of the layer view, returns the point in
* pixels measured from the
top left corner of the root layer, in the coordinate system of
the
*
layer itself. This method is used by the viewport controller as part of the process of
*
translating touch events to Gecko's coordinate system
.
* pixels measured from the
last scroll position we sent to Gecko, in CSS pixels. Assuming
the
*
events being sent to Gecko are processed in FIFO order, this calculation should always be
*
correct
.
*/
public
PointF
convertViewPointToLayerPoint
(
PointF
viewPoint
)
{
if
(
mRootLayer
==
null
)
return
null
;
ImmutableViewportMetrics
viewportMetrics
=
mViewportMetrics
;
// Undo the transforms.
PointF
origin
=
viewportMetrics
.
getOrigin
();
PointF
newPoint
=
new
PointF
(
origin
.
x
,
origin
.
y
);
float
zoom
=
viewportMetrics
.
zoomFactor
;
viewPoint
.
x
/=
zoom
;
viewPoint
.
y
/=
zoom
;
newPoint
.
offset
(
viewPoint
.
x
,
viewPoint
.
y
);
Rect
rootPosition
=
mRootLayer
.
getPosition
();
newPoint
.
offset
(-
rootPosition
.
left
,
-
rootPosition
.
top
);
return
newPoint
;
}
/*
* Gesture detection. This is handled only at a high level in this class; we dispatch to the
* pan/zoom controller to do the dirty work.
*/
public
boolean
onTouchEvent
(
MotionEvent
event
)
{
int
action
=
event
.
getAction
();
PointF
point
=
new
PointF
(
event
.
getX
(),
event
.
getY
());
// this will only match the first touchstart in a series
if
((
action
&
MotionEvent
.
ACTION_MASK
)
==
MotionEvent
.
ACTION_DOWN
)
{
initialTouchLocation
=
point
;
allowDefaultActions
=
!
mWaitForTouchListeners
;
// if we have a timer, this may be a double tap,
// cancel the current timer but don't clear the event queue
if
(
allowDefaultTimer
!=
null
)
{
allowDefaultTimer
.
cancel
();
}
else
{
// if we don't have a timer, make sure we remove any old events
mView
.
clearEventQueue
();
}
allowDefaultTimer
=
new
Timer
();
allowDefaultTimer
.
schedule
(
new
TimerTask
()
{
public
void
run
()
{
post
(
new
Runnable
()
{
public
void
run
()
{
preventPanning
(
false
);
}
});
}
},
mTimeout
);
}
// After the initial touch, ignore touch moves until they exceed a minimum distance.
if
(
initialTouchLocation
!=
null
&&
(
action
&
MotionEvent
.
ACTION_MASK
)
==
MotionEvent
.
ACTION_MOVE
)
{
if
(
PointUtils
.
subtract
(
point
,
initialTouchLocation
).
length
()
>
PanZoomController
.
PAN_THRESHOLD
)
{
initialTouchLocation
=
null
;
}
else
{
return
!
allowDefaultActions
;
}
}
// send the event to content
if
(
mOnTouchListener
!=
null
)
mOnTouchListener
.
onTouch
(
mView
,
event
);
return
!
allowDefaultActions
;
}
public
void
preventPanning
(
boolean
aValue
)
{
if
(
allowDefaultTimer
!=
null
)
{
allowDefaultTimer
.
cancel
();
allowDefaultTimer
=
null
;
}
if
(
aValue
==
allowDefaultActions
)
{
allowDefaultActions
=
!
aValue
;
if
(
aValue
)
{
mView
.
clearEventQueue
();
mPanZoomController
.
cancelTouch
();
}
else
{
mView
.
processEventQueue
();
}
}
}
public
void
setWaitForTouchListeners
(
boolean
aValue
)
{
mWaitForTouchListeners
=
aValue
;
ViewportMetrics
geckoViewport
=
mLayerClient
.
getGeckoViewportMetrics
();
PointF
geckoOrigin
=
geckoViewport
.
getOrigin
();
float
geckoZoom
=
geckoViewport
.
getZoomFactor
();
// viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page.
// Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
// geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from
// the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
// the current Gecko coordinate in CSS pixels.
PointF
layerPoint
=
new
PointF
(
((
viewPoint
.
x
+
origin
.
x
)
/
zoom
)
-
(
geckoOrigin
.
x
/
geckoZoom
),
((
viewPoint
.
y
+
origin
.
y
)
/
zoom
)
-
(
geckoOrigin
.
y
/
geckoZoom
));
return
layerPoint
;
}
/** Retrieves whether we should show checkerboard checks or not. */
...
...
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java
Dosyayı görüntüle @
2c15cb70
...
...
@@ -44,7 +44,6 @@ import android.graphics.Bitmap;
import
android.graphics.PixelFormat
;
import
android.opengl.GLSurfaceView
;
import
android.util.Log
;
import
android.view.GestureDetector
;
import
android.view.KeyEvent
;
import
android.view.MotionEvent
;
import
android.view.SurfaceHolder
;
...
...
@@ -53,10 +52,8 @@ import android.view.inputmethod.EditorInfo;
import
android.view.inputmethod.InputConnection
;
import
org.libreoffice.LibreOfficeMainActivity
;
import
org.mozilla.gecko.ui.SimpleScaleGestureDetector
;
import
java.nio.IntBuffer
;
import
java.util.LinkedList
;
/**
* A view rendered by the layer compositor.
...
...
@@ -67,18 +64,16 @@ import java.util.LinkedList;
* Note that LayerView is accessed by Robocop via reflection.
*/
public
class
LayerView
extends
SurfaceView
implements
SurfaceHolder
.
Callback
{
private
static
String
LOGTAG
=
"GeckoLayerView"
;
private
Context
mContext
;
private
LayerController
mController
;
private
TouchEventHandler
mTouchEventHandler
;
private
GLController
mGLController
;
private
InputConnectionHandler
mInputConnectionHandler
;
private
LayerRenderer
mRenderer
;
private
GestureDetector
mGestureDetector
;
private
SimpleScaleGestureDetector
mScaleGestureDetector
;
private
long
mRenderTime
;
private
boolean
mRenderTimeReset
;
private
static
String
LOGTAG
=
"GeckoLayerView"
;
/* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */
private
LinkedList
<
MotionEvent
>
mEventQueue
=
new
LinkedList
<
MotionEvent
>();
/* Must be a PAINT_xxx constant */
private
int
mPaintState
=
PAINT_NONE
;
...
...
@@ -101,11 +96,8 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
mGLController
=
new
GLController
(
this
);
mContext
=
context
;
mController
=
controller
;
mTouchEventHandler
=
new
TouchEventHandler
(
context
,
this
,
mController
);
mRenderer
=
new
LayerRenderer
(
this
);
mGestureDetector
=
new
GestureDetector
(
context
,
controller
.
getGestureListener
());
mScaleGestureDetector
=
new
SimpleScaleGestureDetector
(
controller
.
getScaleGestureListener
());
mGestureDetector
.
setOnDoubleTapListener
(
controller
.
getDoubleTapListener
());
mInputConnectionHandler
=
null
;
setFocusable
(
true
);
...
...
@@ -114,43 +106,13 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
createGLThread
();
}
private
void
addToEventQueue
(
MotionEvent
event
)
{
MotionEvent
copy
=
MotionEvent
.
obtain
(
event
);
mEventQueue
.
add
(
copy
);
}
public
void
processEventQueue
()
{
MotionEvent
event
=
mEventQueue
.
poll
();
while
(
event
!=
null
)
{
processEvent
(
event
);
event
=
mEventQueue
.
poll
();
}
}
public
void
clearEventQueue
()
{
mEventQueue
.
clear
();
}
@Override
public
boolean
onTouchEvent
(
MotionEvent
event
)
{
if
(
mController
.
onTouchEvent
(
event
))
{
addToEventQueue
(
event
);
return
true
;
}
return
processEvent
(
event
);
}
private
boolean
processEvent
(
MotionEvent
event
)
{
if
(
mGestureDetector
.
onTouchEvent
(
event
))
return
true
;
mScaleGestureDetector
.
onTouchEvent
(
event
);
if
(
mScaleGestureDetector
.
isInProgress
())
return
true
;
mController
.
getPanZoomController
().
onTouchEvent
(
event
);
return
true
;
return
mTouchEventHandler
.
handleEvent
(
event
);
}
public
LayerController
getController
()
{
return
mController
;
}
public
TouchEventHandler
getTouchEventHandler
()
{
return
mTouchEventHandler
;
}
/** The LayerRenderer calls this to indicate that the window has changed size. */
public
void
setViewportSize
(
IntSize
size
)
{
...
...
@@ -215,11 +177,6 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
}
}
public
void
changeCheckerboardBitmap
(
Bitmap
bitmap
)
{
mRenderer
.
resetCheckerboard
();
mRenderer
.
setCheckerboardBitmap
(
bitmap
);
}
public
void
addLayer
(
Layer
layer
)
{
mRenderer
.
addLayer
(
layer
);
}
...
...
@@ -390,4 +347,9 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
super
(
e
);
}
}
public
void
changeCheckerboardBitmap
(
Bitmap
bitmap
)
{
mRenderer
.
resetCheckerboard
();
mRenderer
.
setCheckerboardBitmap
(
bitmap
);
}
}
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java
0 → 100644
Dosyayı görüntüle @
2c15cb70
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package
org
.
mozilla
.
gecko
.
gfx
;
import
android.content.Context
;
import
android.os.SystemClock
;
import
android.view.GestureDetector
;
import
android.view.MotionEvent
;
import
android.view.View.OnTouchListener
;
import
android.view.ViewConfiguration
;
import
org.mozilla.gecko.ui.SimpleScaleGestureDetector
;
import
java.util.LinkedList
;
import
java.util.Queue
;
/**
* This class handles incoming touch events from the user and sends them to
* listeners in Gecko and/or performs the "default action" (asynchronous pan/zoom
* behaviour. EVERYTHING IN THIS CLASS MUST RUN ON THE UI THREAD.
*
* In the following code/comments, a "block" of events refers to a contiguous
* sequence of events that starts with a DOWN or POINTER_DOWN and goes up to
* but not including the next DOWN or POINTER_DOWN event.
*
* "Dispatching" an event refers to performing the default actions for the event,
* which at our level of abstraction just means sending it off to the gesture
* detectors and the pan/zoom controller.
*
* If an event is "default-prevented" that means one or more listeners in Gecko
* has called preventDefault() on the event, which means that the default action
* for that event should not occur. Usually we care about a "block" of events being
* default-prevented, which means that the DOWN/POINTER_DOWN event that started
* the block, or the first MOVE event following that, were prevent-defaulted.
*
* A "default-prevented notification" is when we here in Java-land receive a notification
* from gecko as to whether or not a block of events was default-prevented. This happens
* at some point after the first or second event in the block is processed in Gecko.
* This code assumes we get EXACTLY ONE default-prevented notification for each block
* of events.
*/
public
final
class
TouchEventHandler
{
private
static
final
String
LOGTAG
=
"GeckoTouchEventHandler"
;
// The time limit for listeners to respond with preventDefault on touchevents
// before we begin panning the page
private
final
int
EVENT_LISTENER_TIMEOUT
=
ViewConfiguration
.
getLongPressTimeout
();
private
final
LayerView
mView
;
private
final
LayerController
mController
;
private
final
GestureDetector
mGestureDetector
;
private
final
SimpleScaleGestureDetector
mScaleGestureDetector
;
// the queue of events that we are holding on to while waiting for a preventDefault
// notification
private
final
Queue
<
MotionEvent
>
mEventQueue
;
private
final
ListenerTimeoutProcessor
mListenerTimeoutProcessor
;
// the listener we use to notify gecko of touch events
private
OnTouchListener
mOnTouchListener
;
// whether or not we should wait for touch listeners to respond (this state is
// per-tab and is updated when we switch tabs).
private
boolean
mWaitForTouchListeners
;
// true if we should hold incoming events in our queue. this is re-set for every
// block of events, this is cleared once we find out if the block has been
// default-prevented or not (or we time out waiting for that).
private
boolean
mHoldInQueue
;
// true if we should dispatch incoming events to the gesture detector and the pan/zoom
// controller. if this is false, then the current block of events has been
// default-prevented, and we should not dispatch these events (although we'll still send
// them to gecko listeners).
private
boolean
mDispatchEvents
;
// this next variable requires some explanation. strap yourself in.
//
// for each block of events, we do two things: (1) send the events to gecko and expect
// exactly one default-prevented notification in return, and (2) kick off a delayed
// ListenerTimeoutProcessor that triggers in case we don't hear from the listener in
// a timely fashion.
// since events are constantly coming in, we need to be able to handle more than one
// block of events in the queue.
//
// this means that there are ordering restrictions on these that we can take advantage of,
// and need to abide by. blocks of events in the queue will always be in the order that
// the user generated them. default-prevented notifications we get from gecko will be in
// the same order as the blocks of events in the queue. the ListenerTimeoutProcessors that
// have been posted will also fire in the same order as the blocks of events in the queue.
// HOWEVER, we may get multiple default-prevented notifications interleaved with multiple
// ListenerTimeoutProcessor firings, and that interleaving is not predictable.
//
// therefore, we need to make sure that for each block of events, we process the queued
// events exactly once, either when we get the default-prevented notification, or when the
// timeout expires (whichever happens first). there is no way to associate the
// default-prevented notification with a particular block of events other than via ordering,
//
// so what we do to accomplish this is to track a "processing balance", which is the number
// of default-prevented notifications that we have received, minus the number of ListenerTimeoutProcessors
// that have fired. (think "balance" as in teeter-totter balance). this value is:
// - zero when we are in a state where the next default-prevented notification we expect
// to receive and the next ListenerTimeoutProcessor we expect to fire both correspond to
// the next block of events in the queue.
// - positive when we are in a state where we have received more default-prevented notifications
// than ListenerTimeoutProcessors. This means that the next default-prevented notification
// does correspond to the block at the head of the queue, but the next n ListenerTimeoutProcessors
// need to be ignored as they are for blocks we have already processed. (n is the absolute value
// of the balance.)
// - negative when we are in a state where we have received more ListenerTimeoutProcessors than
// default-prevented notifications. This means that the next ListenerTimeoutProcessor that
// we receive does correspond to the block at the head of the queue, but the next n
// default-prevented notifications need to be ignored as they are for blocks we have already
// processed. (n is the absolute value of the balance.)
private
int
mProcessingBalance
;
TouchEventHandler
(
Context
context
,
LayerView
view
,
LayerController
controller
)
{
mView
=
view
;
mController
=
controller
;
mEventQueue
=
new
LinkedList
<
MotionEvent
>();
mGestureDetector
=
new
GestureDetector
(
context
,
controller
.
getGestureListener
());
mScaleGestureDetector
=
new
SimpleScaleGestureDetector
(
controller
.
getScaleGestureListener
());
mListenerTimeoutProcessor
=
new
ListenerTimeoutProcessor
();
mDispatchEvents
=
true
;
mGestureDetector
.
setOnDoubleTapListener
(
controller
.
getDoubleTapListener
());
}
/* This function MUST be called on the UI thread */
public
boolean
handleEvent
(
MotionEvent
event
)
{
// if we don't have gecko listeners, just dispatch the event
// and be done with it, no extra work needed.
if
(
mOnTouchListener
==
null
)
{
dispatchEvent
(
event
);
return
true
;
}
if
(
isDownEvent
(
event
))
{
// this is the start of a new block of events! whee!
mHoldInQueue
=
mWaitForTouchListeners
;
if
(
mHoldInQueue
)
{
// if we're holding the events in the queue, set the timeout so that
// we dispatch these events if we don't get a default-prevented notification
mView
.
postDelayed
(
mListenerTimeoutProcessor
,
EVENT_LISTENER_TIMEOUT
);
}
else
{
// if we're not holding these events, then we still need to pretend like
// we did and had a ListenerTimeoutProcessor fire so that when we get
// the default-prevented notification for this block, it doesn't accidentally
// act upon some other block
mProcessingBalance
++;
}
}
// if we need to hold the events, add it to the queue. if we need to dispatch
// it directly, do that. it is possible that both mHoldInQueue and mDispatchEvents
// are false, in which case we are processing a block of events that we know
// has been default-prevented. in that case we don't keep the events as we don't
// need them (but we still pass them to the gecko listener).
if
(
mHoldInQueue
)
{
mEventQueue
.
add
(
MotionEvent
.
obtain
(
event
));
}
else
if
(
mDispatchEvents
)
{
dispatchEvent
(
event
);
}
// notify gecko of the event
mOnTouchListener
.
onTouch
(
mView
,
event
);
return
true
;
}
/**
* This function is how gecko sends us a default-prevented notification. It is called
* once gecko knows definitively whether the block of events has had preventDefault
* called on it (either on the initial down event that starts the block, or on
* the first event following that down event).
*
* This function MUST be called on the UI thread.
*/
public
void
handleEventListenerAction
(
boolean
allowDefaultAction
)
{
if
(
mProcessingBalance
>
0
)
{
// this event listener that triggered this took too long, and the corresponding
// ListenerTimeoutProcessor runnable already ran for the event in question. the
// block of events this is for has already been processed, so we don't need to
// do anything here.
}
else
{
processEventBlock
(
allowDefaultAction
);
}
mProcessingBalance
--;
}
/* This function MUST be called on the UI thread. */
public
void
setWaitForTouchListeners
(
boolean
aValue
)
{
mWaitForTouchListeners
=
aValue
;
}
/* This function MUST be called on the UI thread. */
public
void
setOnTouchListener
(
OnTouchListener
onTouchListener
)
{
mOnTouchListener
=
onTouchListener
;
}
private
boolean
isDownEvent
(
MotionEvent
event
)
{
int
action
=
(
event
.
getAction
()
&
MotionEvent
.
ACTION_MASK
);
return
(
action
==
MotionEvent
.
ACTION_DOWN
||
action
==
MotionEvent
.
ACTION_POINTER_DOWN
);
}
/**
* Dispatch the event to the gesture detectors and the pan/zoom controller.
*/
private
void
dispatchEvent
(
MotionEvent
event
)
{
if
(
mGestureDetector
.
onTouchEvent
(
event
))
{
return
;
}
mScaleGestureDetector
.
onTouchEvent
(
event
);
if
(
mScaleGestureDetector
.
isInProgress
())
{
return
;
}
mController
.
getPanZoomController
().
onTouchEvent
(
event
);
}
/**
* Process the block of events at the head of the queue now that we know
* whether it has been default-prevented or not.
*/
private
void
processEventBlock
(
boolean
allowDefaultAction
)
{
if
(!
allowDefaultAction
)
{
// if the block has been default-prevented, cancel whatever stuff we had in
// progress in the gesture detector and pan zoom controller
long
now
=
SystemClock
.
uptimeMillis
();
dispatchEvent
(
MotionEvent
.
obtain
(
now
,
now
,
MotionEvent
.
ACTION_CANCEL
,
0
,
0
,
0
));
}
// the odd loop condition is because the first event in the queue will
// always be a DOWN or POINTER_DOWN event, and we want to process all
// the events in the queue starting at that one, up to but not including
// the next DOWN or POINTER_DOWN event.
MotionEvent
event
=
mEventQueue
.
poll
();
while
(
true
)
{
// for each event we process, only dispatch it if the block hasn't been
// default-prevented.
if
(
allowDefaultAction
)
{
dispatchEvent
(
event
);
}
event
=
mEventQueue
.
peek
();
if
(
event
==
null
)
{
// we have processed the backlog of events, and are all caught up.
// now we can set clear the hold flag and set the dispatch flag so
// that the handleEvent() function can do the right thing for all
// remaining events in this block (which is still ongoing) without
// having to put them in the queue.
mHoldInQueue
=
false
;
mDispatchEvents
=
allowDefaultAction
;
break
;
}
if
(
isDownEvent
(
event
))
{
// we have finished processing the block we were interested in.
// now we wait for the next call to processEventBlock
break
;
}
// pop the event we peeked above, as it is still part of the block and
// we want to keep processing
mEventQueue
.
remove
();
}
}
private
class
ListenerTimeoutProcessor
implements
Runnable
{
/* This MUST be run on the UI thread */
public
void
run
()
{
if
(
mProcessingBalance
<
0
)
{
// gecko already responded with default-prevented notification, and so
// the block of events this ListenerTimeoutProcessor corresponds to have
// already been removed from the queue.
}
else
{
processEventBlock
(
true
);
}
mProcessingBalance
++;
}
}
}
android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
Dosyayı görüntüle @
2c15cb70
...
...
@@ -114,7 +114,8 @@ public class PanZoomController
* similar to TOUCHING but after starting a pan */
PANNING_HOLD_LOCKED
,
/* like PANNING_HOLD, but axis lock still in effect */
PINCHING
,
/* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATED_ZOOM
/* animated zoom to a new rect */
ANIMATED_ZOOM
,
/* animated zoom to a new rect */
BOUNCE
/* in a bounce animation */
}
private
final
LayerController
mController
;
...
...
@@ -172,16 +173,32 @@ public class PanZoomController
// if that's the case, abort any animation in progress and re-zoom so that the page
// snaps to edges. for other cases (where the user's finger(s) are down) don't do
// anything special.
if
(
mState
==
PanZoomState
.
FLING
)
{
switch
(
mState
)
{
case
FLING:
mX
.
stopFling
();
mY
.
stopFling
();
// fall through
case
BOUNCE:
case
ANIMATED_ZOOM:
// the zoom that's in progress likely makes no sense any more (such as if
// the screen orientation changed) so abort it
mState
=
PanZoomState
.
NOTHING
;
// fall through
case
NOTHING:
// Don't do animations here; they're distracting and can cause flashes on page
// transitions.
synchronized
(
mController
)
{
mController
.
setViewportMetrics
(
getValidViewportMetrics
());
mController
.
notifyLayerClientOfGeometryChange
();
}
break
;
}
}
/** This must be called on the UI thread. */
public
void
pageSizeUpdated
()
{
if
(
mState
==
PanZoomState
.
NOTHING
)
{
synchronized
(
mController
)
{
ViewportMetrics
validated
=
getValidViewportMetrics
();
if
(!
(
new
ViewportMetrics
(
mController
.
getViewportMetrics
())).
fuzzyEquals
(
validated
))
{
// page size changed such that we are now in overscroll. snap to the
...
...
@@ -191,6 +208,7 @@ public class PanZoomController
}
}
}
}
/*
* Panning/scrolling
...
...
@@ -206,6 +224,7 @@ public class PanZoomController
case
ANIMATED_ZOOM:
return
false
;
case
FLING:
case
BOUNCE:
case
NOTHING:
startTouch
(
event
.
getX
(
0
),
event
.
getY
(
0
),
event
.
getEventTime
());
return
false
;
...
...
@@ -227,6 +246,7 @@ public class PanZoomController
switch
(
mState
)
{
case
NOTHING:
case
FLING:
case
BOUNCE:
// should never happen
Log
.
e
(
LOGTAG
,
"Received impossible touch move while in "
+
mState
);
return
false
;
...
...
@@ -268,6 +288,7 @@ public class PanZoomController
switch
(
mState
)
{
case
NOTHING:
case
FLING:
case
BOUNCE:
// should never happen
Log
.
e
(
LOGTAG
,
"Received impossible touch end while in "
+
mState
);
return
false
;
...
...
@@ -297,6 +318,7 @@ public class PanZoomController
private
boolean
onTouchCancel
(
MotionEvent
event
)
{
mState
=
PanZoomState
.
NOTHING
;
cancelTouch
();
// ensure we snap back if we're overscrolled
bounce
();
return
false
;
...
...
@@ -401,8 +423,11 @@ public class PanZoomController
return
;
}
mState
=
PanZoomState
.
FLING
;
mState
=
PanZoomState
.
BOUNCE
;
// set the animation target *after* setting state BOUNCE, so that
// the getRedrawHint() is returning false and we don't clobber the display
// port we set as a result of this animation target call.
mController
.
setAnimationTarget
(
metrics
);
startAnimationTimer
(
new
BounceRunnable
(
bounceStartMetrics
,
metrics
));
}
...
...
@@ -444,6 +469,10 @@ public class PanZoomController
return
FloatMath
.
sqrt
(
xvel
*
xvel
+
yvel
*
yvel
);
}
public
PointF
getVelocityVector
()
{
return
new
PointF
(
mX
.
getRealVelocity
(),
mY
.
getRealVelocity
());
}
private
boolean
stopped
()
{
return
getVelocity
()
<
STOPPED_THRESHOLD
;
}
...
...
@@ -456,6 +485,9 @@ public class PanZoomController
mX
.
displace
();
mY
.
displace
();
PointF
displacement
=
getDisplacement
();
if
(
FloatUtils
.
fuzzyEquals
(
displacement
.
x
,
0.0f
)
&&
FloatUtils
.
fuzzyEquals
(
displacement
.
y
,
0.0f
))
{
return
;
}
if
(!
mSubscroller
.
scrollBy
(
displacement
))
{
synchronized
(
mController
)
{
mController
.
scrollBy
(
displacement
);
...
...
@@ -510,7 +542,7 @@ public class PanZoomController
* animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
* out.
*/
if
(
mState
!=
PanZoomState
.
FLING
)
{
if
(
mState
!=
PanZoomState
.
BOUNCE
)
{
finishAnimation
();
return
;
}
...
...
@@ -756,7 +788,18 @@ public class PanZoomController
}
public
boolean
getRedrawHint
()
{
return
(
mState
!=
PanZoomState
.
PINCHING
&&
mState
!=
PanZoomState
.
ANIMATED_ZOOM
);
switch
(
mState
)
{
case
PINCHING:
case
ANIMATED_ZOOM:
case
BOUNCE:
// don't redraw during these because the zoom is (or might be, in the case
// of BOUNCE) be changing rapidly and gecko will have to redraw the entire
// display port area. we trigger a force-redraw upon exiting these states.
return
false
;
default
:
// allow redrawing in other states
return
true
;
}
}
@Override
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment