1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org
.mozilla
.gecko
.gfx
;
8 import org
.libreoffice
.LibreOfficeMainActivity
;
9 import android
.graphics
.PointF
;
10 import android
.graphics
.RectF
;
11 import android
.util
.DisplayMetrics
;
13 import org
.libreoffice
.LOKitShell
;
14 import org
.mozilla
.gecko
.ZoomConstraints
;
16 import java
.util
.List
;
18 public class GeckoLayerClient
implements PanZoomTarget
{
19 private static final String LOGTAG
= GeckoLayerClient
.class.getSimpleName();
21 private LayerRenderer mLayerRenderer
;
23 private LibreOfficeMainActivity mContext
;
24 private IntSize mScreenSize
;
25 private DisplayPortMetrics mDisplayPort
;
27 private ComposedTileLayer mLowResLayer
;
28 private ComposedTileLayer mRootLayer
;
30 private boolean mForceRedraw
;
32 /* The current viewport metrics.
33 * This is volatile so that we can read and write to it from different threads.
34 * We avoid synchronization to make getting the viewport metrics from
35 * the compositor as cheap as possible. The viewport is immutable so
36 * we don't need to worry about anyone mutating it while we're reading from it.
38 * 1) reading mViewportMetrics from any thread is fine without synchronization
39 * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
40 * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
41 * case 1 above) you should always first grab a local copy of the reference, and then use
42 * that because mViewportMetrics might get reassigned in between reading the different
44 private volatile ImmutableViewportMetrics mViewportMetrics
;
46 private ZoomConstraints mZoomConstraints
;
48 private boolean mIsReady
;
50 private PanZoomController mPanZoomController
;
51 private LayerView mView
;
52 private final DisplayPortCalculator mDisplayPortCalculator
;
54 public GeckoLayerClient(LibreOfficeMainActivity context
) {
55 // we can fill these in with dummy values because they are always written
56 // to before being read
58 mScreenSize
= new IntSize(0, 0);
59 mDisplayPort
= new DisplayPortMetrics();
60 mDisplayPortCalculator
= new DisplayPortCalculator(mContext
);
63 DisplayMetrics displayMetrics
= context
.getResources().getDisplayMetrics();
64 mViewportMetrics
= new ImmutableViewportMetrics(displayMetrics
);
67 public void setView(LayerView view
) {
69 mPanZoomController
= PanZoomController
.Factory
.create(mContext
, this, view
);
73 public void notifyReady() {
76 mRootLayer
= new DynamicTileLayer(mContext
);
77 mLowResLayer
= new FixedZoomTileLayer(mContext
);
79 mLayerRenderer
= new LayerRenderer(mView
);
81 mView
.setLayerRenderer(mLayerRenderer
);
83 sendResizeEventIfNecessary(false);
84 mView
.requestRender();
87 public void destroy() {
88 mPanZoomController
.destroy();
92 return mIsReady ? mRootLayer
: null;
95 Layer
getLowResLayer() {
96 return mIsReady ? mLowResLayer
: null;
99 public LayerView
getView() {
104 * Returns true if this controller is fine with performing a redraw operation or false if it
105 * would prefer that the action didn't take place.
107 private boolean getRedrawHint() {
109 mForceRedraw
= false;
113 if (!mPanZoomController
.getRedrawHint()) {
116 return mDisplayPortCalculator
.aboutToCheckerboard(mViewportMetrics
, mPanZoomController
.getVelocityVector(), getDisplayPort());
120 * The view calls this function to indicate that the viewport changed size. It must hold the
121 * monitor while calling it.
123 * TODO: Refactor this to use an interface. Expose that interface only to the view and not
124 * to the layer client. That way, the layer client won't be tempted to call this, which might
125 * result in an infinite loop.
127 void setViewportSize(FloatSize size
, boolean forceResizeEvent
) {
128 mViewportMetrics
= mViewportMetrics
.setViewportSize(size
.width
, size
.height
);
129 sendResizeEventIfNecessary(forceResizeEvent
);
132 PanZoomController
getPanZoomController() {
133 return mPanZoomController
;
136 /* Informs Gecko that the screen size has changed.
137 * @param force: If true, a resize event will always be sent, otherwise
138 * it is only sent if size has changed. */
139 private void sendResizeEventIfNecessary(boolean force
) {
140 DisplayMetrics metrics
= mContext
.getResources().getDisplayMetrics();
141 IntSize newScreenSize
= new IntSize(metrics
.widthPixels
, metrics
.heightPixels
);
143 if (!force
&& mScreenSize
.equals(newScreenSize
)) {
147 mScreenSize
= newScreenSize
;
149 LOKitShell
.sendSizeChangedEvent(mScreenSize
.width
, mScreenSize
.height
);
153 * Sets the current page rect. You must hold the monitor while calling this.
155 private void setPageRect(RectF rect
, RectF cssRect
) {
156 // Since the "rect" is always just a multiple of "cssRect" we don't need to
157 // check both; this function assumes that both "rect" and "cssRect" are relative
158 // the zoom factor in mViewportMetrics.
159 if (mViewportMetrics
.getCssPageRect().equals(cssRect
))
162 mViewportMetrics
= mViewportMetrics
.setPageRect(rect
, cssRect
);
164 // Page size is owned by the layer client, so no need to notify it of
167 post(new Runnable() {
169 mPanZoomController
.pageRectUpdated();
170 mView
.requestRender();
175 private void adjustViewport(DisplayPortMetrics displayPort
) {
176 ImmutableViewportMetrics metrics
= getViewportMetrics();
178 ImmutableViewportMetrics clampedMetrics
= metrics
.clamp();
180 if (displayPort
== null) {
181 displayPort
= mDisplayPortCalculator
.calculate(metrics
, mPanZoomController
.getVelocityVector());
184 mDisplayPort
= displayPort
;
190 * Aborts any pan/zoom animation that is currently in progress.
192 public void abortPanZoomAnimation() {
193 if (mPanZoomController
!= null) {
194 mView
.post(new Runnable() {
196 mPanZoomController
.abortAnimation();
202 public void setZoomConstraints(ZoomConstraints constraints
) {
203 mZoomConstraints
= constraints
;
206 /** The compositor invokes this function whenever it determines that the page rect
207 * has changed (based on the information it gets from layout). If setFirstPaintViewport
208 * is invoked on a frame, then this function will not be. For any given frame, this
209 * function will be invoked before syncViewportInfo.
211 public void setPageRect(float cssPageLeft
, float cssPageTop
, float cssPageRight
, float cssPageBottom
) {
212 synchronized (getLock()) {
213 RectF cssPageRect
= new RectF(cssPageLeft
, cssPageTop
, cssPageRight
, cssPageBottom
);
214 float ourZoom
= getViewportMetrics().zoomFactor
;
215 setPageRect(RectUtils
.scale(cssPageRect
, ourZoom
), cssPageRect
);
216 // Here the page size of the document has changed, but the document being displayed
217 // is still the same. Therefore, we don't need to send anything to browser.js; any
218 // changes we need to make to the display port will get sent the next time we call
223 private DisplayPortMetrics
getDisplayPort() {
227 public void beginDrawing() {
228 mLowResLayer
.beginTransaction();
229 mRootLayer
.beginTransaction();
232 public void endDrawing() {
233 mLowResLayer
.endTransaction();
234 mRootLayer
.endTransaction();
237 private void geometryChanged() {
238 sendResizeEventIfNecessary(false);
239 if (getRedrawHint()) {
240 adjustViewport(null);
244 /** Implementation of PanZoomTarget */
246 public ImmutableViewportMetrics
getViewportMetrics() {
247 return mViewportMetrics
;
250 /** Implementation of PanZoomTarget */
252 public ZoomConstraints
getZoomConstraints() {
253 return mZoomConstraints
;
256 /** Implementation of PanZoomTarget */
258 public void setAnimationTarget(ImmutableViewportMetrics viewport
) {
260 // We know what the final viewport of the animation is going to be, so
261 // immediately request a draw of that area by setting the display port
262 // accordingly. This way we should have the content pre-rendered by the
263 // time the animation is done.
264 DisplayPortMetrics displayPort
= mDisplayPortCalculator
.calculate(viewport
, null);
265 adjustViewport(displayPort
);
269 /** Implementation of PanZoomTarget
270 * You must hold the monitor while calling this.
273 public void setViewportMetrics(ImmutableViewportMetrics viewport
) {
274 mViewportMetrics
= viewport
;
275 mView
.requestRender();
281 /** Implementation of PanZoomTarget */
283 public void forceRedraw() {
290 /** Implementation of PanZoomTarget */
292 public boolean post(Runnable action
) {
293 return mView
.post(action
);
296 /** Implementation of PanZoomTarget */
298 public Object
getLock() {
302 public PointF
convertViewPointToLayerPoint(PointF viewPoint
) {
303 ImmutableViewportMetrics viewportMetrics
= mViewportMetrics
;
304 PointF origin
= viewportMetrics
.getOrigin();
305 float zoom
= viewportMetrics
.zoomFactor
;
308 ((viewPoint
.x
+ origin
.x
) / zoom
),
309 ((viewPoint
.y
+ origin
.y
) / zoom
));
312 /** Implementation of PanZoomTarget */
314 public boolean isFullScreen() {
318 public void zoomTo(RectF rect
) {
319 if (mPanZoomController
instanceof JavaPanZoomController
) {
320 ((JavaPanZoomController
) mPanZoomController
).animatedZoomTo(rect
);
325 * Move the viewport to the desired point, and change the zoom level.
327 public void moveTo(PointF point
, Float zoom
) {
328 if (mPanZoomController
instanceof JavaPanZoomController
) {
329 ((JavaPanZoomController
) mPanZoomController
).animatedMove(point
, zoom
);
333 public void zoomTo(float pageWidth
, float pageHeight
) {
334 zoomTo(new RectF(0, 0, pageWidth
, pageHeight
));
337 public void forceRender() {
338 mView
.requestRender();
341 /* Root Layer Access */
342 private void reevaluateTiles() {
343 mLowResLayer
.reevaluateTiles(mViewportMetrics
, mDisplayPort
);
344 mRootLayer
.reevaluateTiles(mViewportMetrics
, mDisplayPort
);
347 public void clearAndResetlayers() {
348 mLowResLayer
.clearAndReset();
349 mRootLayer
.clearAndReset();
352 public void invalidateTiles(List
<SubTile
> tilesToInvalidate
, RectF rect
) {
353 mLowResLayer
.invalidateTiles(tilesToInvalidate
, rect
);
354 mRootLayer
.invalidateTiles(tilesToInvalidate
, rect
);