Bug 710096 - Hold the monitor on the layer controller when adjusting and rendering the viewport metrics. r=Cwiiis a=java-only

This commit is contained in:
Patrick Walton 2011-12-13 14:43:08 -08:00
parent 01f2ba5697
commit 2adc6acc0a
4 changed files with 115 additions and 94 deletions

View File

@ -162,28 +162,22 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mGeckoViewport = new ViewportMetrics(viewportObject);
mGeckoViewport.setSize(viewportSize);
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin()));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
LayerController controller = getLayerController();
synchronized (controller) {
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
// Make sure LayerController metrics changes only happen in the
// UI thread.
final LayerController controller = getLayerController();
if (controller != null) {
controller.post(new Runnable() {
@Override
public void run() {
if (onlyUpdatePageSize) {
// Don't adjust page size when zooming unless zoom levels are
// approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor()))
controller.setPageSize(mGeckoViewport.getPageSize());
} else {
controller.setViewportMetrics(mGeckoViewport);
controller.notifyPanZoomControllerOfGeometryChange(true);
}
}
});
if (onlyUpdatePageSize) {
// Don't adjust page size when zooming unless zoom levels are
// approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(),
mGeckoViewport.getZoomFactor()))
controller.setPageSize(mGeckoViewport.getPageSize());
} else {
controller.setViewportMetrics(mGeckoViewport);
controller.notifyPanZoomControllerOfGeometryChange(true);
}
}
} catch (JSONException e) {
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);

View File

@ -58,12 +58,13 @@ import android.view.GestureDetector;
import android.view.ScaleGestureDetector;
import android.view.View.OnTouchListener;
import java.lang.Math;
import java.util.ArrayList;
/**
* The layer controller manages a tile that represents the visible page. It does panning and
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
* to a higher-level view.
*
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
*/
public class LayerController {
private static final String LOGTAG = "GeckoLayerController";
@ -155,7 +156,8 @@ public class LayerController {
}
/**
* The view calls this to indicate that the viewport changed size.
* The view calls this function to indicate that the viewport changed size. It must hold the
* monitor while calling it.
*
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
* to the layer client. That way, the layer client won't be tempted to call this, which might
@ -173,6 +175,7 @@ public class LayerController {
mView.requestRender();
}
/** Scrolls the viewport to the given point. You must hold the monitor while calling this. */
public void scrollTo(PointF point) {
mViewportMetrics.setOrigin(point);
notifyLayerClientOfGeometryChange();
@ -181,6 +184,7 @@ public class LayerController {
mView.requestRender();
}
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
public void scrollBy(PointF point) {
PointF origin = mViewportMetrics.getOrigin();
origin.offset(point.x, point.y);
@ -192,6 +196,7 @@ public class LayerController {
mView.requestRender();
}
/** Sets the current viewport. You must hold the monitor while calling this. */
public void setViewport(RectF viewport) {
mViewportMetrics.setViewport(viewport);
notifyLayerClientOfGeometryChange();
@ -200,6 +205,7 @@ public class LayerController {
mView.requestRender();
}
/** Sets the current page size. You must hold the monitor while calling this. */
public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return;
@ -215,7 +221,8 @@ public class LayerController {
/**
* Sets the entire viewport metrics at once. This function does not notify the layer client or
* the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
* notifyPanZoomControllerOfGeometryChange() after calling this.
* notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
* while calling this.
*/
public void setViewportMetrics(ViewportMetrics viewport) {
mViewportMetrics = new ViewportMetrics(viewport);
@ -223,10 +230,15 @@ public class LayerController {
mView.requestRender();
}
/** Scales the viewport. You must hold the monitor while calling this. */
public void scaleTo(float zoomFactor) {
scaleWithFocus(zoomFactor, new PointF(0,0));
}
/**
* 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.
*/
public void scaleWithFocus(float zoomFactor, PointF focus) {
mViewportMetrics.scaleTo(zoomFactor, focus);
@ -237,6 +249,10 @@ public class LayerController {
mView.requestRender();
}
/**
* Sets the viewport origin and scales in one operation. You must hold the monitor while
* calling this.
*/
public void scaleWithOrigin(float zoomFactor, PointF origin) {
mViewportMetrics.setOrigin(origin);
scaleTo(zoomFactor);

View File

@ -132,9 +132,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
/**
* Called whenever a new frame is about to be drawn.
*
* FIXME: This is racy. Layers and page sizes can be modified by the pan/zoom controller while
* this is going on.
*/
public void onDrawFrame(GL10 gl) {
long frameStartTime = SystemClock.uptimeMillis();
@ -142,63 +139,68 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
TextureReaper.get().reap(gl);
LayerController controller = mView.getController();
Layer rootLayer = controller.getRoot();
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
RenderContext screenContext = createScreenContext();
if (!pageContext.fuzzyEquals(mLastPageContext)) {
// the viewport or page changed, so show the scrollbars again
// as per UX decision
mVertScrollLayer.unfade();
mHorizScrollLayer.unfade();
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
} else if (mFadeRunnable.timeToFade()) {
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
if (stillFading) {
mFadeRunnable.scheduleNextFadeFrame();
synchronized (controller) {
Layer rootLayer = controller.getRoot();
RenderContext pageContext = createPageContext();
if (!pageContext.fuzzyEquals(mLastPageContext)) {
// the viewport or page changed, so show the scrollbars again
// as per UX decision
mVertScrollLayer.unfade();
mHorizScrollLayer.unfade();
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
} else if (mFadeRunnable.timeToFade()) {
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
if (stillFading) {
mFadeRunnable.scheduleNextFadeFrame();
}
}
mLastPageContext = pageContext;
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFrameRateLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/* Draw the drop shadow, if we need to. */
Rect pageRect = getPageRect();
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(),
pageRect.height());
if (!untransformedPageRect.contains(controller.getViewport()))
mShadowLayer.draw(pageContext);
/* Draw the checkerboard. */
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
mCheckerboardLayer.draw(screenContext);
/* Draw the layer the client added to us. */
if (rootLayer != null)
rootLayer.draw(pageContext);
gl.glDisable(GL10.GL_SCISSOR_TEST);
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height)
mVertScrollLayer.draw(pageContext);
/* Draw the horizontal scrollbar. */
if (pageRect.width() > screenSize.width)
mHorizScrollLayer.draw(pageContext);
}
mLastPageContext = pageContext;
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFrameRateLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/* Draw the drop shadow, if we need to. */
Rect pageRect = getPageRect();
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(), pageRect.height());
if (!untransformedPageRect.contains(controller.getViewport()))
mShadowLayer.draw(pageContext);
/* Draw the checkerboard. */
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
mCheckerboardLayer.draw(screenContext);
/* Draw the layer the client added to us. */
if (rootLayer != null)
rootLayer.draw(pageContext);
gl.glDisable(GL10.GL_SCISSOR_TEST);
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height)
mVertScrollLayer.draw(pageContext);
/* Draw the horizontal scrollbar. */
if (pageRect.width() > screenSize.width)
mHorizScrollLayer.draw(pageContext);
/* Draw the FPS. */
if (mShowFrameRate) {

View File

@ -560,7 +560,9 @@ public class PanZoomController
GeckoAppShell.sendEventToGecko(e);
mOverrideScrollAck = false;
} else {
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
synchronized (mController) {
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
}
}
mX.displacement = mY.displacement = 0;
@ -592,18 +594,22 @@ public class PanZoomController
/* Performs one frame of a bounce animation. */
private void advanceBounce() {
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mController.setViewportMetrics(newMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame++;
synchronized (mController) {
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mController.setViewportMetrics(newMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame++;
}
}
/* Concludes a bounce animation and snaps the viewport into place. */
private void finishBounce() {
mController.setViewportMetrics(mBounceEndMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame = -1;
synchronized (mController) {
mController.setViewportMetrics(mBounceEndMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame = -1;
}
}
}
@ -881,11 +887,14 @@ public class PanZoomController
else
spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
float newZoomFactor = mController.getZoomFactor() * spanRatio;
synchronized (mController) {
float newZoomFactor = mController.getZoomFactor() * spanRatio;
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
mLastZoomFocus.y - detector.getFocusY()));
mController.scaleWithFocus(newZoomFactor, new PointF(detector.getFocusX(), detector.getFocusY()));
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
mLastZoomFocus.y - detector.getFocusY()));
PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
mController.scaleWithFocus(newZoomFactor, focus);
}
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());