Bug 703141 - Reinstate zooming and add CSS zooming after viewport change. r=kats

This patch reinstates pinch-zooming and adds CSS re-scaling so that after
zooming, the page is rendered at the scaled resolution and you get clear text.

--HG--
rename : mobile/android/base/gfx/IntSize.java => mobile/android/base/gfx/FloatSize.java
This commit is contained in:
Chris Lord 2011-11-23 19:07:47 +00:00
Родитель c5ca0523a3
Коммит 22a6438a2a
17 изменённых файлов: 477 добавлений и 212 удалений

Просмотреть файл

@ -0,0 +1,46 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Lord <chrislord.net@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
import java.lang.Math;
public final class FloatUtils {
public static boolean fuzzyEquals(float a, float b) {
return (Math.abs(a - b) < 1e-6);
}
}

Просмотреть файл

@ -40,6 +40,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerController;
@ -534,41 +535,24 @@ abstract public class GeckoApp
if (getLayerController().getLayerClient() != mSoftwareLayerClient)
return;
IntSize pageSize = getLayerController().getPageSize();
Rect visibleArea = getLayerController().getViewport();
Point offset = getLayerController().getViewportMetrics().getViewportOffset();
ViewportMetrics viewport = mSoftwareLayerClient.getGeckoViewportMetrics();
String uri = lastHistoryEntry.mUri;
String title = lastHistoryEntry.mTitle;
SharedPreferences prefs = getPlaceholderPrefs();
Editor editor = prefs.edit();
String uri = lastHistoryEntry.mUri;
String title = lastHistoryEntry.mTitle;
String lastUri = prefs.getString("last-uri", "");
String lastTitle = prefs.getString("last-title", uri);
// see if we can bail.
if (uri.equals(lastUri) && title.equals(lastTitle))
return;
editor.putString("last-uri", uri);
editor.putString("last-title", title);
if (pageSize != null) {
editor.putInt("page-width", pageSize.width);
editor.putInt("page-height", pageSize.height);
}
if (visibleArea != null) {
editor.putInt("viewport-left", visibleArea.left);
editor.putInt("viewport-top", visibleArea.top);
editor.putInt("viewport-right", visibleArea.right);
editor.putInt("viewport-bottom", visibleArea.bottom);
}
if (offset != null) {
editor.putInt("viewport-offset-x", offset.x);
editor.putInt("viewport-offset-y", offset.y);
if (viewport != null) {
/* XXX Saving this viewport means there may be a slight
* discrepancy between what the user sees when shutting down
* and what they see when starting up, but it oughtn't be much.
*
* The alternative is to do a transformation between the two.
*/
editor.putString("viewport", viewport.toJSON());
}
Log.i(LOGTAG, "Saving:: " + uri + " " + title);
@ -1067,7 +1051,7 @@ abstract public class GeckoApp
if (mGeckoLayout.indexOfChild(view) == -1) {
lp = new PluginLayoutParams((int) w, (int) h, (int)x, (int)y);
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true);
lp.repositionFromVisibleRect(RectUtils.round(mLayerController.getViewport()), mLayerController.getZoomFactor(), true);
view.setWillNotDraw(false);
if (view instanceof SurfaceView) {
@ -1082,7 +1066,7 @@ abstract public class GeckoApp
} else {
lp = (PluginLayoutParams)view.getLayoutParams();
lp.reset((int)x, (int)y, (int)w, (int)h);
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, true);
lp.repositionFromVisibleRect(RectUtils.round(mLayerController.getViewport()), mLayerController.getZoomFactor(), true);
try {
mGeckoLayout.updateViewLayout(view, lp);
} catch (IllegalArgumentException e) {
@ -1124,7 +1108,7 @@ abstract public class GeckoApp
public void repositionPluginViews(boolean resize, boolean setVisible) {
for (View view : mPluginViews) {
PluginLayoutParams lp = (PluginLayoutParams)view.getLayoutParams();
lp.repositionFromVisibleRect(mLayerController.getViewport(), 1.0f/*mLayerController.getZoomFactor()*/, resize);
lp.repositionFromVisibleRect(RectUtils.round(mLayerController.getViewport()), mLayerController.getZoomFactor(), resize);
if (setVisible) {
view.setVisibility(View.VISIBLE);

Просмотреть файл

@ -56,6 +56,7 @@ JAVAFILES = \
DoorHanger.java \
DoorHangerPopup.java \
Favicons.java \
FloatUtils.java \
GeckoApp.java \
GeckoAppShell.java \
GeckoConnectivityReceiver.java \
@ -77,6 +78,7 @@ JAVAFILES = \
gfx/CairoGLInfo.java \
gfx/CairoImage.java \
gfx/CairoUtils.java \
gfx/FloatSize.java \
gfx/GeckoSoftwareLayerClient.java \
gfx/InputConnectionHandler.java \
gfx/IntSize.java \

Просмотреть файл

@ -0,0 +1,73 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.FloatUtils;
import org.json.JSONException;
import org.json.JSONObject;
public class FloatSize {
public final float width, height;
public FloatSize(FloatSize size) { width = size.width; height = size.height; }
public FloatSize(IntSize size) { width = size.width; height = size.height; }
public FloatSize(float aWidth, float aHeight) { width = aWidth; height = aHeight; }
public FloatSize(JSONObject json) {
try {
width = (float)json.getDouble("width");
height = (float)json.getDouble("height");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() { return "(" + width + "," + height + ")"; }
public boolean fuzzyEquals(FloatSize size) {
return (FloatUtils.fuzzyEquals(size.width, width) &&
FloatUtils.fuzzyEquals(size.height, height));
}
public FloatSize scale(float factor) {
return new FloatSize(width * factor, height * factor);
}
}

Просмотреть файл

@ -43,7 +43,9 @@ import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
@ -136,24 +138,32 @@ public class GeckoSoftwareLayerClient extends LayerClient {
*/
public void endDrawing(int x, int y, int width, int height, String metadata) {
try {
LayerController controller = getLayerController();
if (controller == null)
return;
try {
JSONObject metadataObject = new JSONObject(metadata);
mGeckoViewport = new ViewportMetrics(metadataObject);
mTileLayer.setOrigin(mGeckoViewport.getDisplayportOrigin());
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin()));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
// Make sure LayerController metrics changes only happen in the
// UI thread.
final LayerController controller = getLayerController();
if (controller != null) {
if (mNewContent) {
mNewContent = false;
controller.setViewportMetrics(mGeckoViewport);
} else {
controller.setPageSize(mGeckoViewport.getPageSize());
}
controller.post(new Runnable() {
@Override
public void run() {
if (mNewContent) {
mNewContent = false;
controller.setViewportMetrics(mGeckoViewport);
} else {
// Don't adjust page size when zooming unless zoom levels are
// approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor()))
controller.setPageSize(mGeckoViewport.getPageSize());
}
}
});
}
} catch (JSONException e) {
throw new RuntimeException(e);
@ -166,6 +176,11 @@ public class GeckoSoftwareLayerClient extends LayerClient {
}
}
public ViewportMetrics getGeckoViewportMetrics() {
// Return a copy, as we modify this inside the Gecko thread
return new ViewportMetrics(mGeckoViewport);
}
public void geckoLoadedNewContent() {
mNewContent = true;
}
@ -236,9 +251,12 @@ public class GeckoSoftwareLayerClient extends LayerClient {
}
private void adjustViewport() {
ViewportMetrics viewportMetrics = getLayerController().getViewportMetrics();
Point viewportOffset = viewportMetrics.getOptimumViewportOffset();
ViewportMetrics viewportMetrics =
new ViewportMetrics(getLayerController().getViewportMetrics());
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset();
viewportMetrics.setViewportOffset(viewportOffset);
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON());
GeckoAppShell.sendEventToGecko(event);

Просмотреть файл

@ -37,8 +37,10 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.Math;
public class IntSize {
public final int width, height;
@ -46,6 +48,11 @@ public class IntSize {
public IntSize(IntSize size) { width = size.width; height = size.height; }
public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; }
public IntSize(FloatSize size) {
width = Math.round(size.width);
height = Math.round(size.height);
}
public IntSize(JSONObject json) {
try {
width = json.getInt("width");

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -47,14 +48,17 @@ public abstract class Layer {
private boolean mInTransaction;
private Point mOrigin;
private Point mNewOrigin;
private float mResolution;
private float mNewResolution;
public Layer() {
mTransactionLock = new ReentrantLock();
mOrigin = new Point(0, 0);
mResolution = 1.0f;
}
/** Draws the layer. Automatically applies the translation. */
public final void draw(GL10 gl) {
/** Updates the layer. */
public final void update(GL10 gl) {
if (mTransactionLock.isHeldByCurrentThread()) {
throw new RuntimeException("draw() called while transaction lock held by this " +
"thread?!");
@ -67,9 +71,17 @@ public abstract class Layer {
mTransactionLock.unlock();
}
}
}
gl.glPushMatrix();
/** Sets the transformation for the layer. */
public final void transform(GL10 gl) {
gl.glScalef(1.0f / mResolution, 1.0f / mResolution, 1.0f);
gl.glTranslatef(mOrigin.x, mOrigin.y, 0.0f);
}
/** Draws the layer. Automatically applies the transformation. */
public final void draw(GL10 gl) {
gl.glPushMatrix();
onDraw(gl);
@ -88,6 +100,7 @@ public abstract class Layer {
throw new RuntimeException("Nested transactions are not supported");
mTransactionLock.lock();
mInTransaction = true;
mNewResolution = mResolution;
}
/** Call this when you're done modifying the layer. */
@ -115,6 +128,22 @@ public abstract class Layer {
mNewOrigin = newOrigin;
}
/** Returns the current layer's resolution. */
public float getResolution() {
return mResolution;
}
/**
* Sets the layer resolution. This value is used to determine how many pixels per
* device pixel this layer was rendered at. This will be reflected by scaling by
* the reciprocal of the resolution in the layer's transform() function.
* Only valid inside a transaction. */
public void setResolution(float newResolution) {
if (!mInTransaction)
throw new RuntimeException("setResolution() is only valid inside a transaction");
mNewResolution = newResolution;
}
/**
* Subclasses implement this method to perform drawing.
*
@ -132,6 +161,7 @@ public abstract class Layer {
mOrigin = mNewOrigin;
mNewOrigin = null;
}
mResolution = mNewResolution;
}
}

Просмотреть файл

@ -110,19 +110,26 @@ public class LayerController {
public Context getContext() { return mContext; }
public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public Rect getViewport() {
public RectF getViewport() {
return mViewportMetrics.getViewport();
}
public IntSize getViewportSize() {
Rect viewport = getViewport();
return new IntSize(viewport.width(), viewport.height());
public FloatSize getViewportSize() {
return mViewportMetrics.getSize();
}
public IntSize getPageSize() {
public FloatSize getPageSize() {
return mViewportMetrics.getPageSize();
}
public PointF getOrigin() {
return mViewportMetrics.getOrigin();
}
public float getZoomFactor() {
return mViewportMetrics.getZoomFactor();
}
public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); }
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
@ -144,7 +151,7 @@ public class LayerController {
* to the layer client. That way, the layer client won't be tempted to call this, which might
* result in an infinite loop.
*/
public void setViewportSize(IntSize size) {
public void setViewportSize(FloatSize size) {
mViewportMetrics.setSize(size);
notifyLayerClientOfGeometryChange();
@ -152,21 +159,28 @@ public class LayerController {
}
public void scrollTo(PointF point) {
mViewportMetrics.setOrigin(new Point(Math.round(point.x),
Math.round(point.y)));
mViewportMetrics.setOrigin(point);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void scrollBy(PointF point) {
PointF origin = mViewportMetrics.getOrigin();
origin.offset(point.x, point.y);
mViewportMetrics.setOrigin(origin);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void setViewport(Rect viewport) {
public void setViewport(RectF viewport) {
mViewportMetrics.setViewport(viewport);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged();
}
public void setPageSize(IntSize size) {
if (mViewportMetrics.getPageSize().equals(size))
public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return;
mViewportMetrics.setPageSize(size);
@ -184,6 +198,14 @@ public class LayerController {
mPanZoomController.geometryChanged();
}
public void scaleTo(float zoomFactor, PointF focus) {
mViewportMetrics.scaleTo(zoomFactor, focus);
// We assume the zoom level will only be modified by the
// PanZoomController, so no need to notify it of this change.
notifyLayerClientOfGeometryChange();
}
public boolean post(Runnable action) { return mView.post(action); }
public void setOnTouchListener(OnTouchListener onTouchListener) {
@ -204,7 +226,7 @@ public class LayerController {
* would prefer that the action didn't take place.
*/
public boolean getRedrawHint() {
return aboutToCheckerboard();
return true;//aboutToCheckerboard();
}
private RectF getTileRect() {
@ -231,7 +253,8 @@ public class LayerController {
return null;
// Undo the transforms.
PointF newPoint = new PointF(mViewportMetrics.getOrigin());
PointF origin = mViewportMetrics.getOrigin();
PointF newPoint = new PointF(origin.x, origin.y);
newPoint.offset(viewPoint.x, viewPoint.y);
Point rootOrigin = mRootLayer.getOrigin();

Просмотреть файл

@ -48,6 +48,8 @@ import org.mozilla.gecko.gfx.TextureReaper;
import org.mozilla.gecko.gfx.TextLayer;
import org.mozilla.gecko.gfx.TileLayer;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.opengl.GLSurfaceView;
@ -108,38 +110,48 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
TextureReaper.get().reap(gl);
LayerController controller = mView.getController();
Rect pageRect = getPageRect();
Layer rootLayer = controller.getRoot();
/* Update layers */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFPSLayer.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. */
setupPageTransform(gl);
setupPageTransform(gl, false);
mShadowLayer.draw(gl);
/* Draw the checkerboard. */
Rect clampedPageRect = clampToScreen(pageRect);
IntSize screenSize = controller.getViewportSize();
Rect pageRect = getPageRect();
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(clampedPageRect.left, screenSize.height - clampedPageRect.bottom,
clampedPageRect.width(), clampedPageRect.height());
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
gl.glLoadIdentity();
mCheckerboardLayer.draw(gl);
/* Draw the layer the client added to us. */
setupPageTransform(gl);
Layer rootLayer = controller.getRoot();
if (rootLayer != null)
if (rootLayer != null) {
gl.glLoadIdentity();
setupPageTransform(gl, true);
rootLayer.transform(gl);
rootLayer.draw(gl);
}
gl.glDisable(GL10.GL_SCISSOR_TEST);
gl.glEnable(GL10.GL_BLEND);
/* Draw the vertical scrollbar */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height) {
mVertScrollLayer.drawVertical(gl, screenSize, pageRect);
}
@ -156,38 +168,41 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
gl.glDisable(GL10.GL_BLEND);
}
private void setupPageTransform(GL10 gl) {
private void setupPageTransform(GL10 gl, boolean scale) {
LayerController controller = mView.getController();
Rect viewport = controller.getViewport();
//float zoomFactor = controller.getZoomFactor();
gl.glLoadIdentity();
//gl.glScalef(zoomFactor, zoomFactor, 1.0f);
gl.glTranslatef(-viewport.left, -viewport.top, 0.0f);
PointF origin = controller.getOrigin();
gl.glTranslatef(-origin.x, -origin.y, 0.0f);
if (scale) {
float zoomFactor = controller.getZoomFactor();
gl.glScalef(zoomFactor, zoomFactor, 1.0f);
}
}
private Rect getPageRect() {
LayerController controller = mView.getController();
float zoomFactor = 1.0f;//controller.getZoomFactor();
Rect viewport = controller.getViewport();
IntSize pageSize = controller.getPageSize();
int x = (int)Math.round(-zoomFactor * viewport.left);
int y = (int)Math.round(-zoomFactor * viewport.top);
return new Rect(x, y,
x + (int)Math.round(zoomFactor * pageSize.width),
y + (int)Math.round(zoomFactor * pageSize.height));
Point origin = PointUtils.round(controller.getOrigin());
IntSize pageSize = new IntSize(controller.getPageSize());
origin.negate();
return new Rect(origin.x, origin.y,
origin.x + pageSize.width, origin.y + pageSize.height);
}
private Rect clampToScreen(Rect rect) {
private Rect transformToScissorRect(Rect rect) {
LayerController controller = mView.getController();
IntSize screenSize = controller.getViewportSize();
IntSize screenSize = new IntSize(controller.getViewportSize());
int left = Math.max(0, rect.left);
int top = Math.max(0, rect.top);
int right = Math.min(screenSize.width, rect.right);
int bottom = Math.min(screenSize.height, rect.bottom);
return new Rect(left, top, right, bottom);
return new Rect(left, screenSize.height - bottom, right,
(screenSize.height - bottom) + (bottom - top));
}
public void onSurfaceChanged(GL10 gl, final int width, final int height) {

Просмотреть файл

@ -37,6 +37,7 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.InputConnectionHandler;
import org.mozilla.gecko.gfx.LayerController;
import android.content.Context;
@ -91,7 +92,7 @@ public class LayerView extends GLSurfaceView {
/** The LayerRenderer calls this to indicate that the window has changed size. */
public void setViewportSize(IntSize size) {
mController.setViewportSize(size);
mController.setViewportSize(new FloatSize(size));
}
public void setInputConnectionHandler(InputConnectionHandler handler) {

Просмотреть файл

@ -37,6 +37,7 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import javax.microedition.khronos.opengles.GL10;
import java.nio.FloatBuffer;
@ -50,7 +51,7 @@ public class NinePatchTileLayer extends TileLayer {
private FloatBuffer mSideTexCoordBuffer, mSideVertexBuffer;
private FloatBuffer mTopTexCoordBuffer, mTopVertexBuffer;
private LayerController mLayerController;
private IntSize mPageSize;
private FloatSize mPageSize;
private static final int PATCH_SIZE = 16;
private static final int TEXTURE_SIZE = 48;
@ -91,7 +92,7 @@ public class NinePatchTileLayer extends TileLayer {
public NinePatchTileLayer(LayerController layerController, CairoImage image) {
super(false, image);
mPageSize = new IntSize(1, 1);
mPageSize = new FloatSize(1.0f, 1.0f);
mLayerController = layerController;
mSideTexCoordBuffer = createBuffer(SIDE_TEX_COORDS);
@ -125,8 +126,8 @@ public class NinePatchTileLayer extends TileLayer {
@Override
protected void onTileDraw(GL10 gl) {
IntSize pageSize = mLayerController.getPageSize();
if (!pageSize.equals(mPageSize)) {
FloatSize pageSize = mLayerController.getPageSize();
if (!pageSize.fuzzyEquals(mPageSize)) {
mPageSize = pageSize;
recreateVertexBuffers();
}

Просмотреть файл

@ -39,19 +39,22 @@ package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoUtils;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.GeckoApp;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.nio.ByteBuffer;
@ -60,8 +63,11 @@ import java.nio.ByteBuffer;
* is up, then we hand off control to it.
*/
public class PlaceholderLayerClient extends LayerClient {
private static final String LOGTAG = "PlaceholderLayerClient";
private Context mContext;
private ViewportMetrics mViewport;
private boolean mViewportUnknown;
private int mWidth, mHeight, mFormat;
private ByteBuffer mBuffer;
private FetchImageTask mTask;
@ -69,19 +75,19 @@ public class PlaceholderLayerClient extends LayerClient {
private PlaceholderLayerClient(Context context) {
mContext = context;
SharedPreferences prefs = GeckoApp.mAppContext.getPlaceholderPrefs();
IntSize pageSize = new IntSize(prefs.getInt("page-width", 995),
prefs.getInt("page-height", 1250));
Rect viewport = new Rect(prefs.getInt("viewport-left", 0),
prefs.getInt("viewport-top", 0),
prefs.getInt("viewport-right", 1),
prefs.getInt("viewport-bottom", 1));
Point offset = new Point(prefs.getInt("viewport-offset-x", 0),
prefs.getInt("viewport-offset-y", 0));
mViewport = new ViewportMetrics();
mViewport.setPageSize(pageSize);
mViewport.setViewport(viewport);
mViewport.setViewportOffset(offset);
mViewportUnknown = true;
if (prefs.contains("viewport")) {
try {
JSONObject viewportObject = new JSONObject(prefs.getString("viewport", null));
mViewport = new ViewportMetrics(viewportObject);
mViewportUnknown = false;
} catch (JSONException e) {
Log.e(LOGTAG, "Error parsing saved viewport!");
mViewport = new ViewportMetrics();
}
} else {
mViewport = new ViewportMetrics();
}
}
public static PlaceholderLayerClient createInstance(Context context) {
@ -116,6 +122,13 @@ public class PlaceholderLayerClient extends LayerClient {
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * bpp);
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
if (mViewportUnknown) {
mViewport.setPageSize(new FloatSize(mWidth, mHeight));
if (getLayerController() != null)
getLayerController().setPageSize(mViewport.getPageSize());
}
return null;
}
@ -125,7 +138,7 @@ public class PlaceholderLayerClient extends LayerClient {
SingleTileLayer tileLayer = new SingleTileLayer(image);
tileLayer.beginTransaction();
tileLayer.setOrigin(mViewport.getDisplayportOrigin());
tileLayer.setOrigin(PointUtils.round(mViewport.getDisplayportOrigin()));
tileLayer.endTransaction();
getLayerController().setRoot(tileLayer);
@ -141,6 +154,8 @@ public class PlaceholderLayerClient extends LayerClient {
public void setLayerController(LayerController layerController) {
super.setLayerController(layerController);
if (mViewportUnknown)
mViewport.setViewport(layerController.getViewport());
layerController.setViewportMetrics(mViewport);
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);

Просмотреть файл

@ -37,7 +37,9 @@
package org.mozilla.gecko.gfx;
import android.graphics.Point;
import android.graphics.PointF;
import java.lang.Math;
public final class PointUtils {
public static PointF add(PointF one, PointF two) {
@ -51,5 +53,9 @@ public final class PointUtils {
public static PointF scale(PointF point, float factor) {
return new PointF(point.x * factor, point.y * factor);
}
public static Point round(PointF point) {
return new Point(Math.round(point.x), Math.round(point.y));
}
}

Просмотреть файл

@ -88,4 +88,9 @@ public final class RectUtils {
x + (rect.width() * scale),
y + (rect.height() * scale));
}
public static Rect round(RectF rect) {
return new Rect(Math.round(rect.left), Math.round(rect.top),
Math.round(rect.right), Math.round(rect.bottom));
}
}

Просмотреть файл

@ -39,9 +39,12 @@
package org.mozilla.gecko.gfx;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import org.mozilla.gecko.gfx.IntSize;
import android.graphics.RectF;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.RectUtils;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
@ -51,51 +54,57 @@ import android.util.Log;
* the page viewport for the Gecko layer client to use.
*/
public class ViewportMetrics {
private IntSize mPageSize;
private Rect mViewportRect;
private Point mViewportOffset;
private FloatSize mPageSize;
private RectF mViewportRect;
private PointF mViewportOffset;
private float mZoomFactor;
public ViewportMetrics() {
mPageSize = new IntSize(LayerController.TILE_WIDTH,
LayerController.TILE_HEIGHT);
mViewportRect = new Rect(0, 0, 1, 1);
mViewportOffset = new Point(0, 0);
mPageSize = new FloatSize(LayerController.TILE_WIDTH,
LayerController.TILE_HEIGHT);
mViewportRect = new RectF(0, 0, 1, 1);
mViewportOffset = new PointF(0, 0);
mZoomFactor = 1.0f;
}
public ViewportMetrics(ViewportMetrics viewport) {
mPageSize = new IntSize(viewport.getPageSize());
mViewportRect = new Rect(viewport.getViewport());
mViewportOffset = new Point(viewport.getViewportOffset());
mPageSize = new FloatSize(viewport.getPageSize());
mViewportRect = new RectF(viewport.getViewport());
PointF offset = viewport.getViewportOffset();
mViewportOffset = new PointF(offset.x, offset.y);
mZoomFactor = viewport.getZoomFactor();
}
public ViewportMetrics(JSONObject json) throws JSONException {
int x = json.getInt("x");
int y = json.getInt("y");
int width = json.getInt("width");
int height = json.getInt("height");
int pageWidth = json.getInt("pageWidth");
int pageHeight = json.getInt("pageHeight");
int offsetX = json.getInt("offsetX");
int offsetY = json.getInt("offsetY");
float x = (float)json.getDouble("x");
float y = (float)json.getDouble("y");
float width = (float)json.getDouble("width");
float height = (float)json.getDouble("height");
float pageWidth = (float)json.getDouble("pageWidth");
float pageHeight = (float)json.getDouble("pageHeight");
float offsetX = (float)json.getDouble("offsetX");
float offsetY = (float)json.getDouble("offsetY");
float zoom = (float)json.getDouble("zoom");
mPageSize = new IntSize(pageWidth, pageHeight);
mViewportRect = new Rect(x, y, x + width, y + height);
mViewportOffset = new Point(offsetX, offsetY);
mPageSize = new FloatSize(pageWidth, pageHeight);
mViewportRect = new RectF(x, y, x + width, y + height);
mViewportOffset = new PointF(offsetX, offsetY);
mZoomFactor = zoom;
}
public Point getOptimumViewportOffset() {
public PointF getOptimumViewportOffset() {
// XXX We currently always position the viewport in the centre of the
// displayport, but we might want to optimise this during panning
// to minimise checkerboarding.
Point optimumOffset =
new Point((LayerController.TILE_WIDTH - mViewportRect.width()) / 2,
(LayerController.TILE_HEIGHT - mViewportRect.height()) / 2);
new Point((int)Math.round((LayerController.TILE_WIDTH - mViewportRect.width()) / 2),
(int)Math.round((LayerController.TILE_HEIGHT - mViewportRect.height()) / 2));
/* XXX Until bug #524925 is fixed, changing the viewport origin will
* probably cause things to be slower than just having a smaller usable
* displayport.
*/
Rect viewport = getClampedViewport();
Rect viewport = RectUtils.round(getClampedViewport());
// Make sure this offset won't cause wasted pixels in the displayport
// (i.e. make sure the resultant displayport intersects with the page
@ -110,29 +119,29 @@ public class ViewportMetrics {
else if (optimumOffset.y + viewport.bottom > mPageSize.height)
optimumOffset.y -= (mPageSize.height - (optimumOffset.y + viewport.bottom));
return optimumOffset;
return new PointF(optimumOffset);
}
public Point getOrigin() {
return new Point(mViewportRect.left, mViewportRect.top);
public PointF getOrigin() {
return new PointF(mViewportRect.left, mViewportRect.top);
}
public Point getDisplayportOrigin() {
return new Point(mViewportRect.left - mViewportOffset.x,
mViewportRect.top - mViewportOffset.y);
public PointF getDisplayportOrigin() {
return new PointF(mViewportRect.left - mViewportOffset.x,
mViewportRect.top - mViewportOffset.y);
}
public IntSize getSize() {
return new IntSize(mViewportRect.width(), mViewportRect.height());
public FloatSize getSize() {
return new FloatSize(mViewportRect.width(), mViewportRect.height());
}
public Rect getViewport() {
public RectF getViewport() {
return mViewportRect;
}
/** Returns the viewport rectangle, clamped within the page-size. */
public Rect getClampedViewport() {
Rect clampedViewport = new Rect(mViewportRect);
public RectF getClampedViewport() {
RectF clampedViewport = new RectF(mViewportRect);
// While the viewport size ought to never exceed the page size, we
// do the clamping in this order to make sure that the origin is
@ -150,41 +159,61 @@ public class ViewportMetrics {
return clampedViewport;
}
public Point getViewportOffset() {
public PointF getViewportOffset() {
return mViewportOffset;
}
public IntSize getPageSize() {
public FloatSize getPageSize() {
return mPageSize;
}
public void setPageSize(IntSize pageSize) {
public float getZoomFactor() {
return mZoomFactor;
}
public void setPageSize(FloatSize pageSize) {
mPageSize = pageSize;
}
public void setViewport(Rect viewport) {
public void setViewport(RectF viewport) {
mViewportRect = viewport;
}
public void setOrigin(Point origin) {
public void setOrigin(PointF origin) {
mViewportRect.set(origin.x, origin.y,
origin.x + mViewportRect.width(),
origin.y + mViewportRect.height());
}
public void setSize(IntSize size) {
public void setSize(FloatSize size) {
mViewportRect.right = mViewportRect.left + size.width;
mViewportRect.bottom = mViewportRect.top + size.height;
}
public void setViewportOffset(Point offset) {
public void setViewportOffset(PointF offset) {
mViewportOffset = offset;
}
public boolean equals(ViewportMetrics viewport) {
return mViewportRect.equals(viewport.getViewport()) &&
mPageSize.equals(viewport.getPageSize()) &&
mViewportOffset.equals(viewport.getViewportOffset());
public void setZoomFactor(float zoomFactor) {
mZoomFactor = zoomFactor;
}
/* This will set the zoom factor and re-scale page-size and viewport offset
* accordingly. The given focus will remain at the same point on the screen
* after scaling.
*/
public void scaleTo(float newZoomFactor, PointF focus) {
float scaleFactor = newZoomFactor / mZoomFactor;
mPageSize = mPageSize.scale(scaleFactor);
PointF origin = getOrigin();
origin.offset(focus.x, focus.y);
origin = PointUtils.scale(origin, scaleFactor);
origin.offset(-focus.x, -focus.y);
setOrigin(origin);
mZoomFactor = newZoomFactor;
}
public String toJSON() {
@ -196,7 +225,8 @@ public class ViewportMetrics {
", \"pageHeight\" : " + mPageSize.height +
", \"offsetX\" : " + mViewportOffset.x +
", \"offsetY\" : " + mViewportOffset.y +
"}";
", \"zoom\" : " + mZoomFactor +
" }";
}
}

Просмотреть файл

@ -38,11 +38,13 @@
package org.mozilla.gecko.ui;
import org.json.JSONObject;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@ -88,10 +90,8 @@ public class PanZoomController
private Timer mFlingTimer;
private Axis mX, mY;
/* The span at the first zoom event (in unzoomed page coordinates). */
private float mInitialZoomSpan;
/* The zoom focus at the first zoom event (in unzoomed page coordinates). */
private PointF mInitialZoomFocus;
/* The zoom focus at the first zoom event (in page coordinates). */
private PointF mLastZoomFocus;
private enum PanZoomState {
NOTHING, /* no touch-start events received */
@ -322,8 +322,8 @@ public class PanZoomController
// Populates the viewport info and length in the axes.
private void populatePositionAndLength() {
IntSize pageSize = mController.getPageSize();
RectF visibleRect = new RectF(mController.getViewport());
FloatSize pageSize = mController.getPageSize();
RectF visibleRect = mController.getViewport();
mX.setPageLength(pageSize.width);
mX.viewportPos = visibleRect.left;
@ -379,11 +379,6 @@ public class PanZoomController
return 1.0f - excess / (viewportLength * SNAP_LIMIT);
}
private static boolean floatsApproxEqual(float a, float b) {
// account for floating point rounding errors
return Math.abs(a - b) < 1e-6;
}
// Physics information for one axis (X or Y).
private static class Axis {
public enum FlingStates {
@ -411,13 +406,13 @@ public class PanZoomController
public float viewportPos;
private float mViewportLength;
private int mScreenLength;
private int mPageLength;
private float mPageLength;
public FlingStates getFlingState() { return mFlingState; }
public void setViewportLength(float viewportLength) { mViewportLength = viewportLength; }
public void setScreenLength(int screenLength) { mScreenLength = screenLength; }
public void setPageLength(int pageLength) { mPageLength = pageLength; }
public void setPageLength(float pageLength) { mPageLength = pageLength; }
private float getViewportEnd() { return viewportPos + mViewportLength; }
@ -458,7 +453,7 @@ public class PanZoomController
}
float excess = getExcess();
if (floatsApproxEqual(excess, 0.0f))
if (FloatUtils.fuzzyEquals(excess, 0.0f))
mFlingState = FlingStates.STOPPED;
else
mFlingState = FlingStates.WAITING_TO_SNAP;
@ -483,7 +478,7 @@ public class PanZoomController
private void scroll() {
// If we aren't overscrolled, just apply friction.
float excess = getExcess();
if (floatsApproxEqual(excess, 0.0f)) {
if (FloatUtils.fuzzyEquals(excess, 0.0f)) {
velocity *= FRICTION;
if (Math.abs(velocity) < 0.1f) {
velocity = 0.0f;
@ -603,47 +598,34 @@ public class PanZoomController
*/
@Override
public boolean onScale(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PINCHING;
float newZoom = detector.getCurrentSpan() / mInitialZoomSpan;
float newZoomFactor = mController.getZoomFactor() *
(detector.getCurrentSpan() / detector.getPreviousSpan());
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
mLastZoomFocus.y - detector.getFocusY()));
mController.scaleTo(newZoomFactor, new PointF(detector.getFocusX(), detector.getFocusY()));
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
IntSize screenSize = mController.getScreenSize();
float x = mInitialZoomFocus.x - (detector.getFocusX() / newZoom);
float y = mInitialZoomFocus.y - (detector.getFocusY() / newZoom);
float width = screenSize.width / newZoom;
float height = screenSize.height / newZoom;
mController.setVisibleRect(x, y, width, height);
mController.notifyLayerClientOfGeometryChange();
populatePositionAndLength();
*/
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PINCHING;
RectF initialZoomRect = mController.getVisibleRect();
float initialZoom = mController.getZoomFactor();
mInitialZoomFocus = new PointF(initialZoomRect.left + (detector.getFocusX() / initialZoom),
initialZoomRect.top + (detector.getFocusY() / initialZoom));
mInitialZoomSpan = detector.getCurrentSpan() / initialZoom;
mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
GeckoApp.mAppContext.hidePluginViews();
*/
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
/*
mState = PanZoomState.PANNING_HOLD_LOCKED;
mX.firstTouchPos = mX.touchPos = detector.getFocusX();
mY.firstTouchPos = mY.touchPos = detector.getFocusY();
GeckoApp.mAppContext.showPluginViews();
*/
}
@Override

Просмотреть файл

@ -901,7 +901,8 @@ function Tab(aURL, aParams) {
this.id = 0;
this.create(aURL, aParams);
this._viewport = { x: 0, y: 0, width: 1, height: 1, offsetX: 0, offsetY: 0,
pageWidth: 1, pageHeight: 1 };
pageWidth: 1, pageHeight: 1, zoom: 1.0 };
this.viewportExcess = { x: 0, y: 0 };
}
Tab.prototype = {
@ -917,6 +918,7 @@ Tab.prototype = {
this.browser.setAttribute("type", "content");
this.browser.style.width = "980px";
this.browser.style.height = "480px";
this.browser.style.MozTransformOrigin = "0 0";
this.vbox.appendChild(this.browser);
// Turn off clipping so we can buffer areas outside of the browser element.
@ -983,11 +985,18 @@ Tab.prototype = {
},
set viewport(aViewport) {
// TODO: Zoom?
// Transform coordinates based on zoom
aViewport.x /= aViewport.zoom;
aViewport.y /= aViewport.zoom;
// Set scroll position
this.browser.contentWindow.scrollTo(aViewport.x, aViewport.y);
// If we've been asked to over-scroll, do it via the transformation
// and store it separately to the viewport.
let excessX = aViewport.x - this.browser.contentWindow.scrollX;
let excessY = aViewport.y - this.browser.contentWindow.scrollY;
// Check if the viewport size/position has changed and set the necessary
// attributes on the browser element.
if (aViewport.width != this._viewport.width) {
@ -1001,36 +1010,54 @@ Tab.prototype = {
let transformChanged = false;
if (aViewport.offsetX != this._viewport.offsetX) {
if ((aViewport.offsetX != this._viewport.offsetX) ||
(excessX != this.viewportExcess.x)) {
this._viewport.offsetX = aViewport.offsetX;
this.viewportExcess.x = excessX;
transformChanged = true;
}
if (aViewport.offsetY != this._viewport.offsetY) {
if ((aViewport.offsetY != this._viewport.offsetY) ||
(excessY != this.viewportExcess.y)) {
this._viewport.offsetY = aViewport.offsetY;
this.viewportExcess.y = excessY;
transformChanged = true;
}
if (aViewport.zoom != this._viewport.zoom) {
this._viewport.zoom = aViewport.zoom;
transformChanged = true;
}
if (transformChanged)
if (transformChanged) {
this.browser.style.MozTransform =
"translate(" + this._viewport.offsetX + "px, " +
this._viewport.offsetY + "px)";
"translate(" + (this._viewport.offsetX) + "px, " +
(this._viewport.offsetY) + "px) " +
"scale(" + this._viewport.zoom + ") " +
"translate(" + (-excessX) + "px, " + (-excessY) + "px)";
}
},
get viewport() {
// Update the viewport to current dimensions
this._viewport.x = this.browser.contentWindow.scrollX;
this._viewport.y = this.browser.contentWindow.scrollY;
this._viewport.x = this.browser.contentWindow.scrollX +
this.viewportExcess.x;
this._viewport.y = this.browser.contentWindow.scrollY +
this.viewportExcess.y;
let doc = this.browser.contentDocument.documentElement;
let pageWidth = this._viewport.width;
let pageHeight = this._viewport.height;
if (doc != null) {
this._viewport.pageWidth = doc.scrollWidth;
this._viewport.pageHeight = doc.scrollHeight;
} else {
this._viewport.pageWidth = this._viewport.width;
this._viewport.pageHeight = this._viewport.height;
pageWidth = Math.max(pageWidth, doc.scrollWidth);
pageHeight = Math.max(pageHeight, doc.scrollHeight);
}
// Transform coordinates based on zoom
this._viewport.x *= this._viewport.zoom;
this._viewport.y *= this._viewport.zoom;
this._viewport.pageWidth = pageWidth * this._viewport.zoom;
this._viewport.pageHeight = pageHeight * this._viewport.zoom;
return this._viewport;
},