/** Implementation of PanZoomTarget */ public void setAnimationTarget(ImmutableViewportMetrics metrics) { if (mGeckoIsReady) { // 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. DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null); adjustViewport(displayPort); } }
/** * This function is invoked by Gecko via JNI; be careful when modifying signature. The compositor * invokes this function just before compositing a frame where the document is different from the * document composited on the last frame. In these cases, the viewport information we have in Java * is no longer valid and needs to be replaced with the new viewport information provided. * setPageRect will never be invoked on the same frame that this function is invoked on; and this * function will always be called prior to syncViewportInfo. */ public void setFirstPaintViewport( float offsetX, float offsetY, float zoom, float pageLeft, float pageTop, float pageRight, float pageBottom, float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { synchronized (this) { final ImmutableViewportMetrics newMetrics = getViewportMetrics() .setViewportOrigin(offsetX, offsetY) .setZoomFactor(zoom) .setPageRect( new RectF(pageLeft, pageTop, pageRight, pageBottom), new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom)); // Since we have switched to displaying a different document, we need to update any // viewport-related state we have lying around. This includes mGeckoViewport and // mViewportMetrics. Usually this information is updated via handleViewportMessage // while we remain on the same document. post( new Runnable() { public void run() { mGeckoViewport = newMetrics; } }); setViewportMetrics(newMetrics); Tab tab = Tabs.getInstance().getSelectedTab(); mView.setBackgroundColor(tab.getBackgroundColor()); setZoomConstraints(tab.getZoomConstraints()); // At this point, we have just switched to displaying a different document than we // we previously displaying. This means we need to abort any panning/zooming animations // that are in progress and send an updated display port request to browser.js as soon // as possible. We accomplish this by passing true to abortPanZoomAnimation, which // sends the request after aborting the animation. The display port request is actually // a full viewport update, which is fine because if browser.js has somehow moved to // be out of sync with this first-paint viewport, then we force them back in sync. abortPanZoomAnimation(); // Indicate that the document is about to be composited so the // LayerView background can be removed. if (mView.getPaintState() == LayerView.PAINT_START) { mView.setPaintState(LayerView.PAINT_BEFORE_FIRST); } } DisplayPortCalculator.resetPageState(); mDrawTimingQueue.reset(); }
/** Attaches to root layer so that Gecko appears. */ public void notifyGeckoReady() { mGeckoIsReady = true; mRootLayer = new VirtualLayer(new IntSize(mView.getWidth(), mView.getHeight())); mLayerRenderer = mView.getRenderer(); mView.setListener(this); sendResizeEventIfNecessary(true); DisplayPortCalculator.initPrefs(); PluginLayer.initPrefs(); }
/** * Returns true if this client is fine with performing a redraw operation or false if it would * prefer that the action didn't take place. */ private boolean getRedrawHint() { if (mForceRedraw) { mForceRedraw = false; return true; } if (!mPanZoomController.getRedrawHint()) { return false; } return DisplayPortCalculator.aboutToCheckerboard( mViewportMetrics, mPanZoomController.getVelocityVector(), mDisplayPort); }
private void adjustViewport(DisplayPortMetrics displayPort) { ImmutableViewportMetrics metrics = getViewportMetrics(); ImmutableViewportMetrics clampedMetrics = metrics.clamp(); if (displayPort == null) { displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector()); } mDisplayPort = displayPort; mGeckoViewport = clampedMetrics; if (mRecordDrawTimes) { mDrawTimingQueue.add(displayPort); } GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(clampedMetrics, displayPort)); }
public DisplayPortMetrics getDisplayPort( boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) { Tabs tabs = Tabs.getInstance(); if (tabs.isSelectedTab(tabs.getTab(tabId)) && isBrowserContentDisplayed) { // for foreground tabs, send the viewport update unless the document // displayed is different from the content document. In that case, just // calculate the display port. return handleViewportMessage( metrics, pageSizeUpdate ? ViewportMessageType.PAGE_SIZE : ViewportMessageType.UPDATE); } else { // for background tabs, request a new display port calculation, so that // when we do switch to that tab, we have the correct display port and // don't need to draw twice (once to allow the first-paint viewport to // get to java, and again once java figures out the display port). return DisplayPortCalculator.calculate(metrics, null); } }
/** * This function is invoked by Gecko via JNI; be careful when modifying signature. The compositor * invokes this function on every frame to figure out what part of the page to display, and to * inform Java of the current display port. Since it is called on every frame, it needs to be * ultra-fast. It avoids taking any locks or allocating any objects. We keep around a * mCurrentViewTransform so we don't need to allocate a new ViewTransform everytime we're called. * NOTE: we might be able to return a ImmutableViewportMetrics which would avoid the copy into * mCurrentViewTransform. */ public ViewTransform syncViewportInfo( int x, int y, int width, int height, float resolution, boolean layersUpdated) { // getViewportMetrics is thread safe so we don't need to synchronize. // We save the viewport metrics here, so we later use it later in // createFrame (which will be called by nsWindow::DrawWindowUnderlay on // the native side, by the compositor). The viewport // metrics can change between here and there, as it's accessed outside // of the compositor thread. mFrameMetrics = getViewportMetrics(); mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft; mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; mRootLayer.setPositionAndResolution(x, y, x + width, y + height, resolution); if (layersUpdated && mRecordDrawTimes) { // If we got a layers update, that means a draw finished. Check to see if the area drawn // matches // one of our requested displayports; if it does calculate the draw time and notify the // DisplayPortCalculator DisplayPortMetrics drawn = new DisplayPortMetrics(x, y, x + width, y + height, resolution); long time = mDrawTimingQueue.findTimeFor(drawn); if (time >= 0) { long now = SystemClock.uptimeMillis(); time = now - time; mRecordDrawTimes = DisplayPortCalculator.drawTimeUpdate(time, width * height); } } if (layersUpdated && mDrawListener != null) { /* Used by robocop for testing purposes */ mDrawListener.drawFinished(); } return mCurrentViewTransform; }
/** Viewport message handler. */ private DisplayPortMetrics handleViewportMessage( ImmutableViewportMetrics messageMetrics, ViewportMessageType type) { synchronized (this) { ImmutableViewportMetrics metrics; ImmutableViewportMetrics oldMetrics = getViewportMetrics(); switch (type) { default: case UPDATE: // Keep the old viewport size metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); abortPanZoomAnimation(); break; case PAGE_SIZE: // adjust the page dimensions to account for differences in zoom // between the rendered content (which is what Gecko tells us) // and our zoom level (which may have diverged). float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor; metrics = oldMetrics.setPageRect( RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect()); break; } final ImmutableViewportMetrics newMetrics = metrics; post( new Runnable() { public void run() { mGeckoViewport = newMetrics; } }); setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE); mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null); } return mDisplayPort; }
// This is called on the Gecko thread to determine if we're still interested // in the update of this display-port to continue. We can return true here // to abort the current update and continue with any subsequent ones. This // is useful for slow-to-render pages when the display-port starts lagging // behind enough that continuing to draw it is wasted effort. public ProgressiveUpdateData progressiveUpdateCallback( boolean aHasPendingNewThebesContent, float x, float y, float width, float height, float resolution, boolean lowPrecision) { // Skip all low precision draws until we're at risk of checkerboarding if (lowPrecision && !mProgressiveUpdateWasInDanger) { mProgressiveUpdateData.abort = true; return mProgressiveUpdateData; } // Reset the checkerboard risk flag if (!lowPrecision && mLastProgressiveUpdateWasLowPrecision) { mProgressiveUpdateWasInDanger = false; } mLastProgressiveUpdateWasLowPrecision = lowPrecision; // Grab a local copy of the last display-port sent to Gecko and the // current viewport metrics to avoid races when accessing them. DisplayPortMetrics displayPort = mDisplayPort; ImmutableViewportMetrics viewportMetrics = mViewportMetrics; mProgressiveUpdateData.setViewport(viewportMetrics); mProgressiveUpdateData.abort = false; // Always abort updates if the resolution has changed. There's no use // in drawing at the incorrect resolution. if (!FloatUtils.fuzzyEquals(resolution, viewportMetrics.zoomFactor)) { Log.d(LOGTAG, "Aborting draw due to resolution change"); mProgressiveUpdateData.abort = true; return mProgressiveUpdateData; } // Store the high precision displayport for comparison when doing low // precision updates. if (!lowPrecision) { if (!FloatUtils.fuzzyEquals(resolution, mProgressiveUpdateDisplayPort.resolution) || !FloatUtils.fuzzyEquals(x, mProgressiveUpdateDisplayPort.getLeft()) || !FloatUtils.fuzzyEquals(y, mProgressiveUpdateDisplayPort.getTop()) || !FloatUtils.fuzzyEquals(x + width, mProgressiveUpdateDisplayPort.getRight()) || !FloatUtils.fuzzyEquals(y + height, mProgressiveUpdateDisplayPort.getBottom())) { mProgressiveUpdateDisplayPort = new DisplayPortMetrics(x, y, x + width, y + height, resolution); } } // XXX All sorts of rounding happens inside Gecko that becomes hard to // account exactly for. Given we align the display-port to tile // boundaries (and so they rarely vary by sub-pixel amounts), just // check that values are within a couple of pixels of the // display-port bounds. // Never abort drawing if we can't be sure we've sent a more recent // display-port. If we abort updating when we shouldn't, we can end up // with blank regions on the screen and we open up the risk of entering // an endless updating cycle. if (Math.abs(displayPort.getLeft() - mProgressiveUpdateDisplayPort.getLeft()) <= 2 && Math.abs(displayPort.getTop() - mProgressiveUpdateDisplayPort.getTop()) <= 2 && Math.abs(displayPort.getBottom() - mProgressiveUpdateDisplayPort.getBottom()) <= 2 && Math.abs(displayPort.getRight() - mProgressiveUpdateDisplayPort.getRight()) <= 2) { return mProgressiveUpdateData; } if (!lowPrecision && !mProgressiveUpdateWasInDanger) { // If we're not doing low precision draws and we're about to // checkerboard, give up and move onto low precision drawing. if (DisplayPortCalculator.aboutToCheckerboard( viewportMetrics, mPanZoomController.getVelocityVector(), mProgressiveUpdateDisplayPort)) { mProgressiveUpdateWasInDanger = true; } } // Abort updates when the display-port no longer contains the visible // area of the page (that is, the viewport cropped by the page // boundaries). // XXX This makes the assumption that we never let the visible area of // the page fall outside of the display-port. if (Math.max(viewportMetrics.viewportRectLeft, viewportMetrics.pageRectLeft) + 1 < x || Math.max(viewportMetrics.viewportRectTop, viewportMetrics.pageRectTop) + 1 < y || Math.min(viewportMetrics.viewportRectRight, viewportMetrics.pageRectRight) - 1 > x + width || Math.min(viewportMetrics.viewportRectBottom, viewportMetrics.pageRectBottom) - 1 > y + height) { Log.d(LOGTAG, "Aborting update due to viewport not in display-port"); mProgressiveUpdateData.abort = true; return mProgressiveUpdateData; } // Abort drawing stale low-precision content if there's a more recent // display-port in the pipeline. if (lowPrecision && !aHasPendingNewThebesContent) { mProgressiveUpdateData.abort = true; } return mProgressiveUpdateData; }