From 1c7bcda5525f710ad016f22995fcd88be6c040aa Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Tue, 17 Jun 2014 13:05:33 +0200 Subject: LOAndroid2: use GLES2, latest GeckoSoftwareLayerClient Additionally add GeckoGLLayerClient as an alternative LayerClient Change-Id: I250b5d806f520231ad9a9b84ef8387e8830bb364 --- android/experimental/LOAndroid/.idea/misc.xml | 3 + android/experimental/LOAndroid2/.idea/modules.xml | 1 + android/experimental/LOAndroid2/LOAndroid2.iml | 12 + android/experimental/LOAndroid2/app/app.iml | 2 +- .../app/src/main/java/org/libreoffice/LOEvent.java | 6 +- .../src/main/java/org/libreoffice/LOKitShell.java | 13 + .../src/main/java/org/libreoffice/LOKitThread.java | 58 +-- .../org/libreoffice/LibreOfficeMainActivity.java | 18 +- .../java/org/mozilla/gecko/gfx/CairoGLInfo.java | 35 +- .../org/mozilla/gecko/gfx/CheckerboardImage.java | 6 +- .../mozilla/gecko/gfx/FlexibleGLSurfaceView.java | 218 +++++++++ .../main/java/org/mozilla/gecko/gfx/FloatSize.java | 25 +- .../java/org/mozilla/gecko/gfx/GLController.java | 279 ++++++++++++ .../main/java/org/mozilla/gecko/gfx/GLThread.java | 181 ++++++++ .../org/mozilla/gecko/gfx/GeckoGLLayerClient.java | 265 +++++++++++ .../org/mozilla/gecko/gfx/GeckoLayerClient.java | 414 +++++++++++++++++ .../gecko/gfx/GeckoSoftwareLayerClient.java | 433 ++++++------------ .../src/main/java/org/mozilla/gecko/gfx/Layer.java | 71 ++- .../org/mozilla/gecko/gfx/LayerController.java | 101 +++-- .../java/org/mozilla/gecko/gfx/LayerRenderer.java | 499 ++++++++++++++------- .../main/java/org/mozilla/gecko/gfx/LayerView.java | 27 +- .../java/org/mozilla/gecko/gfx/MultiTileLayer.java | 172 +++---- .../main/java/org/mozilla/gecko/gfx/RectUtils.java | 47 +- .../java/org/mozilla/gecko/gfx/ScrollbarLayer.java | 282 ++++++++++-- .../org/mozilla/gecko/gfx/SingleTileLayer.java | 51 ++- .../java/org/mozilla/gecko/gfx/TextureReaper.java | 7 +- .../main/java/org/mozilla/gecko/gfx/TileLayer.java | 74 +-- .../java/org/mozilla/gecko/gfx/ViewTransform.java | 51 +++ .../org/mozilla/gecko/gfx/ViewportMetrics.java | 126 +++--- .../java/org/mozilla/gecko/gfx/VirtualLayer.java | 80 ++++ .../org/mozilla/gecko/gfx/WidgetTileLayer.java | 69 ++- .../src/main/java/org/mozilla/gecko/ui/Axis.java | 4 +- .../org/mozilla/gecko/ui/PanZoomController.java | 30 +- .../gecko/ui/SimpleScaleGestureDetector.java | 54 +-- .../mozilla/gecko/ui/SubdocumentScrollHelper.java | 16 +- 35 files changed, 2789 insertions(+), 941 deletions(-) create mode 100644 android/experimental/LOAndroid2/LOAndroid2.iml create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FlexibleGLSurfaceView.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLController.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLThread.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoGLLayerClient.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java create mode 100644 android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java (limited to 'android') diff --git a/android/experimental/LOAndroid/.idea/misc.xml b/android/experimental/LOAndroid/.idea/misc.xml index d0225fc0c171..589a3ed5fd78 100644 --- a/android/experimental/LOAndroid/.idea/misc.xml +++ b/android/experimental/LOAndroid/.idea/misc.xml @@ -3,6 +3,9 @@ + + + diff --git a/android/experimental/LOAndroid2/.idea/modules.xml b/android/experimental/LOAndroid2/.idea/modules.xml index f08135d5d6bf..d5e166fb414b 100644 --- a/android/experimental/LOAndroid2/.idea/modules.xml +++ b/android/experimental/LOAndroid2/.idea/modules.xml @@ -3,6 +3,7 @@ + diff --git a/android/experimental/LOAndroid2/LOAndroid2.iml b/android/experimental/LOAndroid2/LOAndroid2.iml new file mode 100644 index 000000000000..edb62a65fa47 --- /dev/null +++ b/android/experimental/LOAndroid2/LOAndroid2.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/android/experimental/LOAndroid2/app/app.iml b/android/experimental/LOAndroid2/app/app.iml index e89240b42923..34b87a5df4f4 100644 --- a/android/experimental/LOAndroid2/app/app.iml +++ b/android/experimental/LOAndroid2/app/app.iml @@ -1,5 +1,5 @@ - + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOEvent.java index a5396693de89..bf4f98b845da 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOEvent.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOEvent.java @@ -20,7 +20,7 @@ public class LOEvent { ViewportMetrics viewportMetrics; - public LOEvent(int type, int width, int height, int widthPixels, int heightPixels) { + public LOEvent(int type, int width, int height, int widthPixels, int heightPixels, int tileWidth, int tileHeight) { mType = type; mTypeString = "Size Changed"; } @@ -45,8 +45,8 @@ public class LOEvent { return new LOEvent(DRAW, rect); } - public static LOEvent sizeChanged(int width, int height, int widthPixels, int heightPixels) { - return new LOEvent(SIZE_CHANGED, width, height, widthPixels, heightPixels); + public static LOEvent sizeChanged(int width, int height, int widthPixels, int heightPixels, int tileWidth, int tileHeight) { + return new LOEvent(SIZE_CHANGED, width, height, widthPixels, heightPixels, tileWidth, tileHeight); } public static LOEvent tileSize(IntSize tileSize) { diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitShell.java index 69b634cc2231..9dde7906d020 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitShell.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitShell.java @@ -115,4 +115,17 @@ public class LOKitShell { layerController.notifyLayerClientOfGeometryChange();*/ } + + public static void viewSizeChanged() { + } + + public static void scheduleComposite() { + } + + public static void schedulePauseComposition() { + } + + public static void scheduleResumeComposition() { + + } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitThread.java index 37bb51c00926..08c49493cbd8 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LOKitThread.java @@ -4,6 +4,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Rect; import android.util.JsonWriter; import org.mozilla.gecko.gfx.ViewportMetrics; @@ -11,8 +12,6 @@ import org.mozilla.gecko.gfx.ViewportMetrics; import java.io.IOException; import java.io.StringWriter; import java.nio.ByteBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; import java.util.Random; import java.util.concurrent.ConcurrentLinkedQueue; @@ -26,28 +25,11 @@ public class LOKitThread extends Thread { LOKitThread() { } - private void draw() throws InterruptedException { + private boolean draw() throws InterruptedException { final LibreOfficeMainActivity application = LibreOfficeMainActivity.mAppContext; - Bitmap bitmap = application.getSoftwareLayerClient().getLayerController().getDrawable16("dummy_page"); - bitmap = convert(bitmap, Bitmap.Config.RGB_565); - - application.getSoftwareLayerClient().beginDrawing(bitmap.getWidth(), bitmap.getHeight()); - //application.getSoftwareLayerClient().beginDrawing(500,500); - - ByteBuffer buffer = application.getSoftwareLayerClient().getBuffer(); - bitmap.copyPixelsToBuffer(buffer.asIntBuffer()); - - /*short mainColor16 = convertTo16Bit(rand.nextInt()); - - short[] mainPattern = new short[500]; - Arrays.fill(mainPattern, mainColor16); - - buffer.rewind(); - ShortBuffer shortBuffer = buffer.asShortBuffer(); - for (int i = 0; i < 500; i++) { - shortBuffer.put(mainPattern); - }*/ + Bitmap bitmap = application.getLayerClient().getLayerController().getDrawable16("dummy_page"); + bitmap = convert(bitmap, Bitmap.Config.RGB_565); StringWriter stringWriter = new StringWriter(); @@ -64,7 +46,6 @@ public class LOKitThread extends Thread { writer.name("offsetX").value(0); writer.name("offsetY").value(0); writer.name("zoom").value(1.0); - writer.name("allowZoom").value(true); } else { writer.name("x").value(mViewportMetrics.getOrigin().x); writer.name("y").value(mViewportMetrics.getOrigin().y); @@ -75,7 +56,6 @@ public class LOKitThread extends Thread { writer.name("offsetX").value(mViewportMetrics.getViewportOffset().x); writer.name("offsetY").value(mViewportMetrics.getViewportOffset().y); writer.name("zoom").value(mViewportMetrics.getZoomFactor()); - writer.name("allowZoom").value(mViewportMetrics.getAllowZoom()); } writer.name("backgroundColor").value("rgb(255,255,255)"); writer.endObject(); @@ -83,15 +63,24 @@ public class LOKitThread extends Thread { } catch (IOException ex) { } - application.getSoftwareLayerClient().endDrawing(0, 0, bitmap.getWidth(), bitmap.getHeight(), stringWriter.toString(), false); - //application.getSoftwareLayerClient().endDrawing(0, 0, 500, 500, stringWriter.toString(), false); - application.runOnUiThread(new Runnable() { - @Override - public void run() { - application.getSoftwareLayerClient().handleMessage("Viewport:UpdateLater", null); - } - }); + Rect bufferRect = application.getLayerClient().beginDrawing(bitmap.getWidth(), bitmap.getHeight(), 256, 256, stringWriter.toString(), false); + + if (bufferRect == null) { + return false; + } + ByteBuffer buffer = application.getLayerClient().lockBuffer(); + bitmap.copyPixelsToBuffer(buffer.asIntBuffer()); + application.getLayerClient().unlockBuffer(); + application.getLayerClient().endDrawing(0, 0, bitmap.getWidth(), bitmap.getHeight()); + + application.runOnUiThread(new Runnable() { + @Override + public void run() { + application.getLayerClient().handleMessage("Viewport:UpdateLater", null); + } + }); + return true; } private short convertTo16Bit(int color) { @@ -119,9 +108,8 @@ public class LOKitThread extends Thread { if (!gEvents.isEmpty()) { processEvent(gEvents.poll()); } else { - if(!drawn) { - draw(); - drawn = true; + if (!drawn) { + drawn = draw(); } Thread.sleep(100L); } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LibreOfficeMainActivity.java index a4fdf03c6503..934464d2dea6 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/libreoffice/LibreOfficeMainActivity.java @@ -23,7 +23,7 @@ public class LibreOfficeMainActivity extends Activity { private LinearLayout mMainLayout; private RelativeLayout mGeckoLayout; private static LayerController mLayerController; - private static GeckoSoftwareLayerClient mSoftwareLayerClient; + private static GeckoSoftwareLayerClient mLayerClient; private static LOKitThread sLOKitThread; public static LibreOfficeMainActivity mAppContext; @@ -74,8 +74,12 @@ public class LibreOfficeMainActivity extends Activity { if (mLayerController == null) { mLayerController = new LayerController(this); - mSoftwareLayerClient = new GeckoSoftwareLayerClient(this); - mLayerController.setLayerClient(mSoftwareLayerClient); + + Log.e(LOGTAG, "### Creating GeckoSoftwareLayerClient"); + mLayerClient = new GeckoSoftwareLayerClient(this); + Log.e(LOGTAG, "### Done creating GeckoSoftwareLayerClient"); + + mLayerController.setLayerClient(mLayerClient); mGeckoLayout.addView(mLayerController.getView(), 0); } @@ -88,7 +92,11 @@ public class LibreOfficeMainActivity extends Activity { Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up"); } - public static GeckoSoftwareLayerClient getSoftwareLayerClient() { - return mSoftwareLayerClient; + public static GeckoSoftwareLayerClient getLayerClient() { + return mLayerClient; + } + + public static LayerController getLayerController() { + return mLayerController; } } \ No newline at end of file diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java index 16417e17b51f..bd4eedcaf951 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java @@ -39,7 +39,9 @@ package org.mozilla.gecko.gfx; import javax.microedition.khronos.opengles.GL10; -/** Information needed to render Cairo bitmaps using OpenGL ES. */ +/** + * Information needed to render Cairo bitmaps using OpenGL ES. + */ public class CairoGLInfo { public final int internalFormat; public final int format; @@ -47,20 +49,23 @@ public class CairoGLInfo { public CairoGLInfo(int cairoFormat) { switch (cairoFormat) { - case CairoImage.FORMAT_ARGB32: - internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB24: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB16_565: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5; - break; - case CairoImage.FORMAT_A8: - case CairoImage.FORMAT_A1: - throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported"); - default: - throw new RuntimeException("Unknown Cairo format"); + case CairoImage.FORMAT_ARGB32: + internalFormat = format = GL10.GL_RGBA; + type = GL10.GL_UNSIGNED_BYTE; + break; + case CairoImage.FORMAT_RGB24: + internalFormat = format = GL10.GL_RGB; + type = GL10.GL_UNSIGNED_BYTE; + break; + case CairoImage.FORMAT_RGB16_565: + internalFormat = format = GL10.GL_RGB; + type = GL10.GL_UNSIGNED_SHORT_5_6_5; + break; + case CairoImage.FORMAT_A8: + case CairoImage.FORMAT_A1: + throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported"); + default: + throw new RuntimeException("Unknown Cairo format"); } } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CheckerboardImage.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CheckerboardImage.java index f9ff9664bc62..392d7e8d8463 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CheckerboardImage.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/CheckerboardImage.java @@ -38,7 +38,6 @@ package org.mozilla.gecko.gfx; import org.libreoffice.LOKitShell; -//import org.mozilla.gecko.GeckoAppShell; import android.graphics.Color; import java.nio.ByteBuffer; import java.nio.ShortBuffer; @@ -62,7 +61,7 @@ public class CheckerboardImage extends CairoImage { /** Creates a new checkerboard image. */ public CheckerboardImage() { int bpp = CairoUtils.bitsPerPixelForCairoFormat(FORMAT); - mBuffer = /*GeckoAppShell*/LOKitShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); + mBuffer = LOKitShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); update(true, Color.WHITE); } @@ -146,7 +145,7 @@ public class CheckerboardImage extends CairoImage { protected void finalize() throws Throwable { try { if (mBuffer != null) { - /*GeckoAppShell*/LOKitShell.freeDirectBuffer(mBuffer); + LOKitShell.freeDirectBuffer(mBuffer); } } finally { super.finalize(); @@ -168,3 +167,4 @@ public class CheckerboardImage extends CairoImage { return FORMAT; } } + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FlexibleGLSurfaceView.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FlexibleGLSurfaceView.java new file mode 100644 index 000000000000..dc20077b0457 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FlexibleGLSurfaceView.java @@ -0,0 +1,218 @@ +/* -*- 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-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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.GeckoApp; +import android.content.Context; +import android.graphics.PixelFormat; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import org.libreoffice.LibreOfficeMainActivity; + +public class FlexibleGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + private static final String LOGTAG = "GeckoFlexibleGLSurfaceView"; + + private GLSurfaceView.Renderer mRenderer; + private GLThread mGLThread; // Protected by this class's monitor. + private GLController mController; + private Listener mListener; + + public FlexibleGLSurfaceView(Context context) { + super(context); + init(); + } + + public FlexibleGLSurfaceView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + init(); + } + + public void init() { + SurfaceHolder holder = getHolder(); + holder.addCallback(this); + holder.setFormat(PixelFormat.RGB_565); + + mController = new GLController(this); + } + + public void setRenderer(GLSurfaceView.Renderer renderer) { + mRenderer = renderer; + } + + public GLSurfaceView.Renderer getRenderer() { + return mRenderer; + } + + public void setListener(Listener listener) { + mListener = listener; + } + + public synchronized void requestRender() { + if (mGLThread != null) { + mGLThread.renderFrame(); + } + if (mListener != null) { + mListener.renderRequested(); + } + } + + /** + * Creates a Java GL thread. After this is called, the FlexibleGLSurfaceView may be used just + * like a GLSurfaceView. It is illegal to access the controller after this has been called. + */ + public synchronized void createGLThread() { + if (mGLThread != null) { + throw new FlexibleGLSurfaceViewException("createGLThread() called with a GL thread " + + "already in place!"); + } + + Log.e(LOGTAG, "### Creating GL thread!"); + mGLThread = new GLThread(mController); + mGLThread.start(); + notifyAll(); + } + + /** + * Destroys the Java GL thread. Returns a Thread that completes when the Java GL thread is + * fully shut down. + */ + public synchronized Thread destroyGLThread() { + // Wait for the GL thread to be started. + Log.e(LOGTAG, "### Waiting for GL thread to be created..."); + while (mGLThread == null) { + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + Log.e(LOGTAG, "### Destroying GL thread!"); + Thread glThread = mGLThread; + mGLThread.shutdown(); + mGLThread = null; + return glThread; + } + + public synchronized void recreateSurface() { + if (mGLThread == null) { + throw new FlexibleGLSurfaceViewException("recreateSurface() called with no GL " + + "thread active!"); + } + + mGLThread.recreateSurface(); + } + + public synchronized GLController getGLController() { + if (mGLThread != null) { + throw new FlexibleGLSurfaceViewException("getGLController() called with a GL thread " + + "active; shut down the GL thread first!"); + } + + return mController; + } + + public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + mController.sizeChanged(width, height); + if (mGLThread != null) { + mGLThread.surfaceChanged(width, height); + } + + if (mListener != null) { + mListener.surfaceChanged(width, height); + } + } + + public synchronized void surfaceCreated(SurfaceHolder holder) { + mController.surfaceCreated(); + if (mGLThread != null) { + mGLThread.surfaceCreated(); + } + } + + public synchronized void surfaceDestroyed(SurfaceHolder holder) { + mController.surfaceDestroyed(); + if (mGLThread != null) { + mGLThread.surfaceDestroyed(); + } + + if (mListener != null) { + mListener.compositionPauseRequested(); + } + } + + // Called from the compositor thread + public static GLController registerCxxCompositor() { + try { + Log.e(LOGTAG, "### registerCxxCompositor point A"); + System.out.println("register layer comp"); + Log.e(LOGTAG, "### registerCxxCompositor point B"); + FlexibleGLSurfaceView flexView = (FlexibleGLSurfaceView) /*GeckoApp*/LibreOfficeMainActivity.mAppContext.getLayerController().getView(); + Log.e(LOGTAG, "### registerCxxCompositor point C: " + flexView); + try { + flexView.destroyGLThread().join(); + } catch (InterruptedException e) {} + Log.e(LOGTAG, "### registerCxxCompositor point D: " + flexView.getGLController()); + return flexView.getGLController(); + } catch (Exception e) { + Log.e(LOGTAG, "### Exception! " + e); + return null; + } + } + + public interface Listener { + void renderRequested(); + void compositionPauseRequested(); + void compositionResumeRequested(); + void surfaceChanged(int width, int height); + } + + public static class FlexibleGLSurfaceViewException extends RuntimeException { + public static final long serialVersionUID = 1L; + + FlexibleGLSurfaceViewException(String e) { + super(e); + } + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java index 21a712ca7318..5fb73ec18df9 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java @@ -38,21 +38,32 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.util.FloatUtils; import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.gecko.util.FloatUtils; 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(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"); + width = (float) json.getDouble("width"); + height = (float) json.getDouble("height"); } catch (JSONException e) { throw new RuntimeException(e); } @@ -82,7 +93,7 @@ public class FloatSize { */ public FloatSize interpolate(FloatSize to, float t) { return new FloatSize(FloatUtils.interpolate(width, to.width, t), - FloatUtils.interpolate(height, to.height, t)); + FloatUtils.interpolate(height, to.height, t)); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLController.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLController.java new file mode 100644 index 000000000000..e8f201228666 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLController.java @@ -0,0 +1,279 @@ +/* -*- 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-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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 android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; +import javax.microedition.khronos.opengles.GL10; + +public class GLController { + private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + private static final String LOGTAG = "GeckoGLController"; + + private FlexibleGLSurfaceView mView; + private int mGLVersion; + private boolean mSurfaceValid; + private int mWidth, mHeight; + + private EGL10 mEGL; + private EGLDisplay mEGLDisplay; + private EGLConfig mEGLConfig; + private EGLContext mEGLContext; + private EGLSurface mEGLSurface; + + private GL mGL; + + private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4; + + private static final int[] CONFIG_SPEC = { + EGL10.EGL_RED_SIZE, 5, + EGL10.EGL_GREEN_SIZE, 6, + EGL10.EGL_BLUE_SIZE, 5, + EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, + EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + + public GLController(FlexibleGLSurfaceView view) { + mView = view; + mGLVersion = 2; + mSurfaceValid = false; + } + + public void setGLVersion(int version) { + mGLVersion = version; + } + + /** You must call this on the same thread you intend to use OpenGL on. */ + public void initGLContext() { + initEGLContext(); + createEGLSurface(); + } + + public void disposeGLContext() { + if (!mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, + EGL10.EGL_NO_CONTEXT)) { + throw new GLControllerException("EGL context could not be released!"); + } + + if (mEGLSurface != null) { + if (!mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)) { + throw new GLControllerException("EGL surface could not be destroyed!"); + } + + mEGLSurface = null; + } + + if (mEGLContext == null) { + if (!mEGL.eglDestroyContext(mEGLDisplay, mEGLContext)) { + throw new GLControllerException("EGL context could not be destroyed!"); + } + + mGL = null; + mEGLDisplay = null; + mEGLConfig = null; + mEGLContext = null; + } + } + + public GL getGL() { return mEGLContext.getGL(); } + public EGLDisplay getEGLDisplay() { return mEGLDisplay; } + public EGLConfig getEGLConfig() { return mEGLConfig; } + public EGLContext getEGLContext() { return mEGLContext; } + public EGLSurface getEGLSurface() { return mEGLSurface; } + public FlexibleGLSurfaceView getView() { return mView; } + + public boolean hasSurface() { + return mEGLSurface != null; + } + + public boolean swapBuffers() { + return mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface); + } + + public boolean checkForLostContext() { + if (mEGL.eglGetError() != EGL11.EGL_CONTEXT_LOST) { + return false; + } + + mEGLDisplay = null; + mEGLConfig = null; + mEGLContext = null; + mEGLSurface = null; + mGL = null; + return true; + } + + public synchronized void waitForValidSurface() { + while (!mSurfaceValid) { + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public synchronized int getWidth() { + return mWidth; + } + + public synchronized int getHeight() { + return mHeight; + } + + synchronized void surfaceCreated() { + mSurfaceValid = true; + notifyAll(); + } + + synchronized void surfaceDestroyed() { + mSurfaceValid = false; + notifyAll(); + } + + synchronized void sizeChanged(int newWidth, int newHeight) { + mWidth = newWidth; + mHeight = newHeight; + } + + private void initEGL() { + mEGL = (EGL10)EGLContext.getEGL(); + + mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) { + throw new GLControllerException("eglGetDisplay() failed"); + } + + int[] version = new int[2]; + if (!mEGL.eglInitialize(mEGLDisplay, version)) { + throw new GLControllerException("eglInitialize() failed"); + } + + mEGLConfig = chooseConfig(); + } + + private void initEGLContext() { + initEGL(); + + int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, mGLVersion, EGL10.EGL_NONE }; + mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL10.EGL_NO_CONTEXT, + attribList); + if (mEGLContext == null || mEGLContext == EGL10.EGL_NO_CONTEXT) { + throw new GLControllerException("createContext() failed"); + } + } + + private EGLConfig chooseConfig() { + int[] numConfigs = new int[1]; + if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, null, 0, numConfigs) || + numConfigs[0] <= 0) { + throw new GLControllerException("No available EGL configurations"); + } + + EGLConfig[] configs = new EGLConfig[numConfigs[0]]; + if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, configs, numConfigs[0], numConfigs)) { + throw new GLControllerException("No EGL configuration for that specification"); + } + + // Select the first 565 RGB configuration. + int[] red = new int[1], green = new int[1], blue = new int[1]; + for (EGLConfig config : configs) { + mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_RED_SIZE, red); + mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_GREEN_SIZE, green); + mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_BLUE_SIZE, blue); + if (red[0] == 5 && green[0] == 6 && blue[0] == 5) { + return config; + } + } + + throw new GLControllerException("No suitable EGL configuration found"); + } + + private void createEGLSurface() { + SurfaceHolder surfaceHolder = mView.getHolder(); + mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surfaceHolder, null); + if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) { + throw new GLControllerException("EGL window surface could not be created!"); + } + + if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { + throw new GLControllerException("EGL surface could not be made into the current " + + "surface!"); + } + + mGL = mEGLContext.getGL(); + + if (mView.getRenderer() != null) { + mView.getRenderer().onSurfaceCreated((GL10)mGL, mEGLConfig); + mView.getRenderer().onSurfaceChanged((GL10)mGL, mView.getWidth(), mView.getHeight()); + } + } + + // Provides an EGLSurface without assuming ownership of this surface. + private EGLSurface provideEGLSurface() { + if (mEGL == null) { + initEGL(); + } + + SurfaceHolder surfaceHolder = mView.getHolder(); + mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surfaceHolder, null); + if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) { + throw new GLControllerException("EGL window surface could not be created!"); + } + + return mEGLSurface; + } + + public static class GLControllerException extends RuntimeException { + public static final long serialVersionUID = 1L; + + GLControllerException(String e) { + super(e); + } + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLThread.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLThread.java new file mode 100644 index 000000000000..4f788f64ebcb --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GLThread.java @@ -0,0 +1,181 @@ +/* -*- 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-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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 android.opengl.GLSurfaceView; +import android.view.SurfaceHolder; +import javax.microedition.khronos.opengles.GL10; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +// A GL thread managed by Java. It is not necessary to use this class to use the +// FlexibleGLSurfaceView, but it can be helpful, especially if the GL rendering is to be done +// entirely in Java. +class GLThread extends Thread { + private LinkedBlockingQueue mQueue; + private GLController mController; + private boolean mRenderQueued; + + public GLThread(GLController controller) { + mQueue = new LinkedBlockingQueue(); + mController = controller; + } + + @Override + public void run() { + while (true) { + Runnable runnable; + try { + runnable = mQueue.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + runnable.run(); + if (runnable instanceof ShutdownMessage) { + break; + } + } + } + + public void recreateSurface() { + mQueue.add(new RecreateSurfaceMessage()); + } + + public void renderFrame() { + // Make sure there's only one render event in the queue at a time. + synchronized (this) { + if (!mRenderQueued) { + mQueue.add(new RenderFrameMessage()); + mRenderQueued = true; + } + } + } + + public void shutdown() { + mQueue.add(new ShutdownMessage()); + } + + public void surfaceChanged(int width, int height) { + mQueue.add(new SizeChangedMessage(width, height)); + } + + public void surfaceCreated() { + mQueue.add(new SurfaceCreatedMessage()); + } + + public void surfaceDestroyed() { + mQueue.add(new SurfaceDestroyedMessage()); + } + + private void doRecreateSurface() { + mController.disposeGLContext(); + mController.initGLContext(); + } + + private GLSurfaceView.Renderer getRenderer() { + return mController.getView().getRenderer(); + } + + private class RecreateSurfaceMessage implements Runnable { + public void run() { + doRecreateSurface(); + } + } + + private class RenderFrameMessage implements Runnable { + public void run() { + synchronized (GLThread.this) { + mRenderQueued = false; + } + + // Bail out if the surface was lost. + if (mController.getEGLSurface() == null) { + return; + } + + GLSurfaceView.Renderer renderer = getRenderer(); + if (renderer != null) { + renderer.onDrawFrame((GL10)mController.getGL()); + } + + mController.swapBuffers(); + //if (!mController.swapBuffers() && mController.checkForLostContext()) { + // doRecreateSurface(); + //} + } + } + + private class ShutdownMessage implements Runnable { + public void run() { + mController.disposeGLContext(); + mController = null; + } + } + + private class SizeChangedMessage implements Runnable { + private int mWidth, mHeight; + + public SizeChangedMessage(int width, int height) { + mWidth = width; + mHeight = height; + } + + public void run() { + GLSurfaceView.Renderer renderer = getRenderer(); + if (renderer != null) { + renderer.onSurfaceChanged((GL10)mController.getGL(), mWidth, mHeight); + } + } + } + + private class SurfaceCreatedMessage implements Runnable { + public void run() { + if (!mController.hasSurface()) { + mController.initGLContext(); + } + } + } + + private class SurfaceDestroyedMessage implements Runnable { + public void run() { + mController.disposeGLContext(); + } + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoGLLayerClient.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoGLLayerClient.java new file mode 100644 index 000000000000..9e4f376e7ad2 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoGLLayerClient.java @@ -0,0 +1,265 @@ +/* -*- 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 + * + * 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 android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import org.libreoffice.LOEvent; +import org.libreoffice.LOKitShell; + +public class GeckoGLLayerClient extends GeckoLayerClient + implements FlexibleGLSurfaceView.Listener, VirtualLayer.Listener { + private static final String LOGTAG = "GeckoGLLayerClient"; + + private LayerRenderer mLayerRenderer; + private boolean mLayerRendererInitialized; + + public GeckoGLLayerClient(Context context) { + super(context); + } + + @Override + public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight, + String metadata, boolean hasDirectTexture) { + Rect bufferRect = super.beginDrawing(width, height, tileWidth, tileHeight, + metadata, hasDirectTexture); + if (bufferRect == null) { + return null; + } + + // Be sure to adjust the buffer size; if it's not at least as large as the viewport size, + // ViewportMetrics.getOptimumViewportOffset() gets awfully confused and severe display + // corruption results! + if (mBufferSize.width != width || mBufferSize.height != height) { + mBufferSize = new IntSize(width, height); + } + + return bufferRect; + } + + @Override + protected boolean handleDirectTextureChange(boolean hasDirectTexture) { + Log.e(LOGTAG, "### handleDirectTextureChange"); + if (mTileLayer != null) { + return false; + } + + Log.e(LOGTAG, "### Creating virtual layer"); + VirtualLayer virtualLayer = new VirtualLayer(); + virtualLayer.setListener(this); + virtualLayer.setSize(getBufferSize()); + getLayerController().setRoot(virtualLayer); + mTileLayer = virtualLayer; + + sendResizeEventIfNecessary(true); + return true; + } + + @Override + public void setLayerController(LayerController layerController) { + super.setLayerController(layerController); + + LayerView view = layerController.getView(); + view.setListener(this); + + mLayerRenderer = new LayerRenderer(view); + } + + @Override + protected boolean shouldDrawProceed(int tileWidth, int tileHeight) { + Log.e(LOGTAG, "### shouldDrawProceed"); + // Always draw. + return true; + } + + @Override + protected void updateLayerAfterDraw(Rect updatedRect) { + Log.e(LOGTAG, "### updateLayerAfterDraw"); + // Nothing to do. + } + + @Override + protected IntSize getBufferSize() { + View view = (View) getLayerController().getView(); + IntSize size = new IntSize(view.getWidth(), view.getHeight()); + Log.e(LOGTAG, "### getBufferSize " + size); + return size; + } + + @Override + protected IntSize getTileSize() { + Log.e(LOGTAG, "### getTileSize " + getBufferSize()); + return getBufferSize(); + } + + @Override + protected void tileLayerUpdated() { + // Set the new origin and resolution instantly. + mTileLayer.performUpdates(null); + } + + @Override + public Bitmap getBitmap() { + Log.e(LOGTAG, "### getBitmap"); + IntSize size = getBufferSize(); + try { + return Bitmap.createBitmap(size.width, size.height, Bitmap.Config.RGB_565); + } catch (OutOfMemoryError oom) { + Log.e(LOGTAG, "Unable to create bitmap", oom); + return null; + } + } + + @Override + public int getType() { + Log.e(LOGTAG, "### getType"); + return LAYER_CLIENT_TYPE_GL; + } + + public void dimensionsChanged(Point newOrigin, float newResolution) { + Log.e(LOGTAG, "### dimensionsChanged " + newOrigin + " " + newResolution); + } + + /* Informs Gecko that the screen size has changed. */ + @Override + protected void sendResizeEventIfNecessary(boolean force) { + Log.e(LOGTAG, "### sendResizeEventIfNecessary " + force); + + IntSize newSize = getBufferSize(); + if (!force && mScreenSize != null && mScreenSize.equals(newSize)) { + return; + } + + mScreenSize = newSize; + + Log.e(LOGTAG, "### Screen-size changed to " + mScreenSize); + //GeckoEvent event = GeckoEvent.createSizeChangedEvent(mScreenSize.width, mScreenSize.height, + // mScreenSize.width, mScreenSize.height, + // mScreenSize.width, mScreenSize.height); + //GeckoAppShell.sendEventToGecko(event); + LOEvent event = LOEvent.sizeChanged(mScreenSize.width, mScreenSize.height, + mScreenSize.width, mScreenSize.height, + mScreenSize.width, mScreenSize.height); + LOKitShell.sendEvent(event); + + } + + /** + * For Gecko to use. + */ + public ViewTransform getViewTransform() { + Log.e(LOGTAG, "### getViewTransform()"); + + // NB: We don't begin a transaction here because this can be called in a synchronous + // manner between beginDrawing() and endDrawing(), and that will cause a deadlock. + + LayerController layerController = getLayerController(); + synchronized (layerController) { + ViewportMetrics viewportMetrics = layerController.getViewportMetrics(); + PointF viewportOrigin = viewportMetrics.getOrigin(); + Point tileOrigin = mTileLayer.getOrigin(); + float scrollX = viewportOrigin.x; + float scrollY = viewportOrigin.y; + float zoomFactor = viewportMetrics.getZoomFactor(); + Log.e(LOGTAG, "### Viewport metrics = " + viewportMetrics + " tile reso = " + + mTileLayer.getResolution()); + return new ViewTransform(scrollX, scrollY, zoomFactor); + } + } + + public void renderRequested() { + Log.e(LOGTAG, "### Render requested, scheduling composite"); + LOKitShell.scheduleComposite(); + } + + public void compositionPauseRequested() { + Log.e(LOGTAG, "### Scheduling PauseComposition"); + LOKitShell.schedulePauseComposition(); + } + + public void compositionResumeRequested() { + Log.e(LOGTAG, "### Scheduling ResumeComposition"); + LOKitShell.scheduleResumeComposition(); + } + + public void surfaceChanged(int width, int height) { + compositionPauseRequested(); + LayerController layerController = getLayerController(); + layerController.setViewportSize(new FloatSize(width, height)); + compositionResumeRequested(); + renderRequested(); + } + + /** + * For Gecko to use. + */ + public LayerRenderer.Frame createFrame() { + // Create the shaders and textures if necessary. + if (!mLayerRendererInitialized) { + mLayerRenderer.createProgram(); + mLayerRendererInitialized = true; + } + + // Build the contexts and create the frame. + Layer.RenderContext pageContext = mLayerRenderer.createPageContext(); + Layer.RenderContext screenContext = mLayerRenderer.createScreenContext(); + return mLayerRenderer.createFrame(pageContext, screenContext); + } + + /** + * For Gecko to use. + */ + public void activateProgram() { + mLayerRenderer.activateProgram(); + } + + /** + * For Gecko to use. + */ + public void deactivateProgram() { + mLayerRenderer.deactivateProgram(); + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java new file mode 100644 index 000000000000..09349b4eea07 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -0,0 +1,414 @@ +/* -*- 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 + * Chris Lord + * + * 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.libreoffice.LOEvent; +import org.libreoffice.LOKitShell; +import org.libreoffice.LibreOfficeMainActivity; +import org.mozilla.gecko.util.FloatUtils; +//import org.mozilla.gecko.GeckoApp; +//import org.mozilla.gecko.GeckoAppShell; +//import org.mozilla.gecko.GeckoEvent; +import org.mozilla.gecko.GeckoEventListener; +import org.json.JSONException; +import org.json.JSONObject; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.SystemClock; +import android.util.DisplayMetrics; +import android.util.Log; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class GeckoLayerClient extends LayerClient implements GeckoEventListener { + private static final String LOGTAG = "GeckoLayerClient"; + + public static final int LAYER_CLIENT_TYPE_NONE = 0; + public static final int LAYER_CLIENT_TYPE_SOFTWARE = 1; + public static final int LAYER_CLIENT_TYPE_GL = 2; + + protected IntSize mScreenSize; + protected IntSize mBufferSize; + + protected Layer mTileLayer; + + /* The viewport that Gecko is currently displaying. */ + protected ViewportMetrics mGeckoViewport; + + /* The viewport that Gecko will display when drawing is finished */ + protected ViewportMetrics mNewGeckoViewport; + + private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L; + private long mLastViewportChangeTime; + private boolean mPendingViewportAdjust; + private boolean mViewportSizeChanged; + + // mUpdateViewportOnEndDraw is used to indicate that we received a + // viewport update notification while drawing. therefore, when the + // draw finishes, we need to update the entire viewport rather than + // just the page size. this boolean should always be accessed from + // inside a transaction, so no synchronization is needed. + private boolean mUpdateViewportOnEndDraw; + + private String mLastCheckerboardColor; + + private static Pattern sColorPattern; + + /* Used by robocop for testing purposes */ + private DrawListener mDrawListener; + + protected abstract boolean handleDirectTextureChange(boolean hasDirectTexture); + protected abstract boolean shouldDrawProceed(int tileWidth, int tileHeight); + protected abstract void updateLayerAfterDraw(Rect updatedRect); + protected abstract IntSize getBufferSize(); + protected abstract IntSize getTileSize(); + protected abstract void tileLayerUpdated(); + public abstract Bitmap getBitmap(); + public abstract int getType(); + + public GeckoLayerClient(Context context) { + mScreenSize = new IntSize(0, 0); + mBufferSize = new IntSize(0, 0); + } + + /** Attaches the root layer to the layer controller so that Gecko appears. */ + @Override + public void setLayerController(LayerController layerController) { + super.setLayerController(layerController); + + layerController.setRoot(mTileLayer); + if (mGeckoViewport != null) { + layerController.setViewportMetrics(mGeckoViewport); + } + + //GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this); + //GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this); + + sendResizeEventIfNecessary(); + } + + public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight, + String metadata, boolean hasDirectTexture) { + Log.e(LOGTAG, "### beginDrawing " + width + " " + height + " " + tileWidth + " " + + tileHeight + " " + hasDirectTexture); + + // If we've changed surface types, cancel this draw + if (handleDirectTextureChange(hasDirectTexture)) { + Log.e(LOGTAG, "### Cancelling draw due to direct texture change"); + return null; + } + + if (!shouldDrawProceed(tileWidth, tileHeight)) { + Log.e(LOGTAG, "### Cancelling draw due to shouldDrawProceed()"); + return null; + } + + LayerController controller = getLayerController(); + + try { + JSONObject viewportObject = new JSONObject(metadata); + mNewGeckoViewport = new ViewportMetrics(viewportObject); + + Log.e(LOGTAG, "### beginDrawing new Gecko viewport " + mNewGeckoViewport); + + // Update the background color, if it's present. + String backgroundColorString = viewportObject.optString("backgroundColor"); + if (backgroundColorString != null && !backgroundColorString.equals(mLastCheckerboardColor)) { + mLastCheckerboardColor = backgroundColorString; + controller.setCheckerboardColor(parseColorFromGecko(backgroundColorString)); + } + } catch (JSONException e) { + Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata); + return null; + } + + + // Make sure we don't spend time painting areas we aren't interested in. + // Only do this if the Gecko viewport isn't going to override our viewport. + Rect bufferRect = new Rect(0, 0, width, height); + + if (!mUpdateViewportOnEndDraw) { + // First, find out our ideal displayport. We do this by taking the + // clamped viewport origin and taking away the optimum viewport offset. + // This would be what we would send to Gecko if adjustViewport were + // called now. + ViewportMetrics currentMetrics = controller.getViewportMetrics(); + PointF currentBestOrigin = RectUtils.getOrigin(currentMetrics.getClampedViewport()); + PointF viewportOffset = currentMetrics.getOptimumViewportOffset(new IntSize(width, height)); + currentBestOrigin.offset(-viewportOffset.x, -viewportOffset.y); + + Rect currentRect = RectUtils.round(new RectF(currentBestOrigin.x, currentBestOrigin.y, + currentBestOrigin.x + width, currentBestOrigin.y + height)); + + // Second, store Gecko's displayport. + PointF currentOrigin = mNewGeckoViewport.getDisplayportOrigin(); + bufferRect = RectUtils.round(new RectF(currentOrigin.x, currentOrigin.y, + currentOrigin.x + width, currentOrigin.y + height)); + + + // Take the intersection of the two as the area we're interested in rendering. + if (!bufferRect.intersect(currentRect)) { + // If there's no intersection, we have no need to render anything, + // but make sure to update the viewport size. + beginTransaction(mTileLayer); + try { + updateViewport(true); + } finally { + endTransaction(mTileLayer); + } + return null; + } + bufferRect.offset(Math.round(-currentOrigin.x), Math.round(-currentOrigin.y)); + } + + beginTransaction(mTileLayer); + return bufferRect; + } + + /* + * TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require + * a little more JNI magic. + */ + public void endDrawing(int x, int y, int width, int height) { + synchronized (getLayerController()) { + try { + updateViewport(!mUpdateViewportOnEndDraw); + mUpdateViewportOnEndDraw = false; + + Rect rect = new Rect(x, y, x + width, y + height); + updateLayerAfterDraw(rect); + } finally { + endTransaction(mTileLayer); + } + } + Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing"); + + /* Used by robocop for testing purposes */ + if (mDrawListener != null) { + mDrawListener.drawFinished(x, y, width, height); + } + } + + protected void updateViewport(boolean onlyUpdatePageSize) { + // save and restore the viewport size stored in java; never let the + // JS-side viewport dimensions override the java-side ones because + // java is the One True Source of this information, and allowing JS + // to override can lead to race conditions where this data gets clobbered. + FloatSize viewportSize = getLayerController().getViewportSize(); + mGeckoViewport = mNewGeckoViewport; + mGeckoViewport.setSize(viewportSize); + + LayerController controller = getLayerController(); + PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin(); + mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); + mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); + + this.tileLayerUpdated(); + Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + + " getTileViewport " + mGeckoViewport); + + 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.abortPanZoomAnimation(); + } + } + + /* Informs Gecko that the screen size has changed. */ + protected void sendResizeEventIfNecessary(boolean force) { + Log.e(LOGTAG, "### sendResizeEventIfNecessary " + force); + + DisplayMetrics metrics = new DisplayMetrics(); + /*GeckoApp*/LibreOfficeMainActivity.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); + + // Return immediately if the screen size hasn't changed or the viewport + // size is zero (which indicates that the rendering surface hasn't been + // allocated yet). + boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width || + metrics.heightPixels != mScreenSize.height); + boolean viewportSizeValid = (getLayerController() != null && + getLayerController().getViewportSize().isPositive()); + if (!(force || (screenSizeChanged && viewportSizeValid))) { + return; + } + + mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); + IntSize bufferSize = getBufferSize(), tileSize = getTileSize(); + + Log.e(LOGTAG, "### Screen-size changed to " + mScreenSize); + //GeckoEvent event = GeckoEvent.createSizeChangedEvent(bufferSize.width, bufferSize.height, + // metrics.widthPixels, metrics.heightPixels, + // tileSize.width, tileSize.height); + //GeckoAppShell.sendEventToGecko(event); + LOEvent event = LOEvent.sizeChanged(bufferSize.width, bufferSize.height, + metrics.widthPixels, metrics.heightPixels, + tileSize.width, tileSize.height); + } + + // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color + // cannot be parsed, returns white. + private static int parseColorFromGecko(String string) { + if (sColorPattern == null) { + sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); + } + + Matcher matcher = sColorPattern.matcher(string); + if (!matcher.matches()) { + return Color.WHITE; + } + + int r = Integer.parseInt(matcher.group(1)); + int g = Integer.parseInt(matcher.group(2)); + int b = Integer.parseInt(matcher.group(3)); + return Color.rgb(r, g, b); + } + + @Override + public void render() { + adjustViewportWithThrottling(); + } + + private void adjustViewportWithThrottling() { + if (!getLayerController().getRedrawHint()) + return; + + if (mPendingViewportAdjust) + return; + + long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime; + if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) { + getLayerController().getView().postDelayed( + new Runnable() { + public void run() { + mPendingViewportAdjust = false; + adjustViewport(); + } + }, MIN_VIEWPORT_CHANGE_DELAY - timeDelta); + mPendingViewportAdjust = true; + return; + } + + adjustViewport(); + } + + @Override + public void viewportSizeChanged() { + mViewportSizeChanged = true; + } + + private void adjustViewport() { + ViewportMetrics viewportMetrics = + new ViewportMetrics(getLayerController().getViewportMetrics()); + + PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(mBufferSize); + viewportMetrics.setViewportOffset(viewportOffset); + viewportMetrics.setViewport(viewportMetrics.getClampedViewport()); + + //GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(viewportMetrics)); + LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics)); + if (mViewportSizeChanged) { + mViewportSizeChanged = false; + //GeckoAppShell.viewSizeChanged(); + LOKitShell.viewSizeChanged(); + } + + mLastViewportChangeTime = System.currentTimeMillis(); + } + + public void handleMessage(String event, JSONObject message) { + if ("Viewport:UpdateAndDraw".equals(event)) { + Log.e(LOGTAG, "### Java side Viewport:UpdateAndDraw()!"); + mUpdateViewportOnEndDraw = true; + + // Redraw everything. + Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height); + //GeckoAppShell.sendEventToGecko(GeckoEvent.createDrawEvent(rect)); + LOKitShell.sendEvent(LOEvent.draw(rect)); + } else if ("Viewport:UpdateLater".equals(event)) { + Log.e(LOGTAG, "### Java side Viewport:UpdateLater()!"); + mUpdateViewportOnEndDraw = true; + } + } + + @Override + public void geometryChanged() { + /* Let Gecko know if the screensize has changed */ + sendResizeEventIfNecessary(); + render(); + } + + public int getWidth() { + return mBufferSize.width; + } + + public int getHeight() { + return mBufferSize.height; + } + + public ViewportMetrics getGeckoViewportMetrics() { + // Return a copy, as we modify this inside the Gecko thread + if (mGeckoViewport != null) + return new ViewportMetrics(mGeckoViewport); + return null; + } + + private void sendResizeEventIfNecessary() { + sendResizeEventIfNecessary(false); + } + + /** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */ + public void setDrawListener(DrawListener listener) { + mDrawListener = listener; + } + + /** Used by robocop for testing purposes. Not for production use! This is used via reflection by robocop. */ + public interface DrawListener { + public void drawFinished(int x, int y, int width, int height); + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoSoftwareLayerClient.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoSoftwareLayerClient.java index 9385d8185bf0..97cbfb49acf8 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoSoftwareLayerClient.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/GeckoSoftwareLayerClient.java @@ -38,130 +38,70 @@ package org.mozilla.gecko.gfx; +import org.libreoffice.LOKitShell; +import org.mozilla.gecko.gfx.CairoImage; +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.MultiTileLayer; +import org.mozilla.gecko.gfx.PointUtils; +import org.mozilla.gecko.gfx.WidgetTileLayer; +//import org.mozilla.gecko.GeckoAppShell; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PointF; +import android.graphics.Point; import android.graphics.Rect; -import android.util.DisplayMetrics; +import android.graphics.RectF; import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; -import org.libreoffice.LOEvent; -import org.libreoffice.LOKitShell; -import org.libreoffice.LibreOfficeMainActivity; -import org.mozilla.gecko.GeckoEventListener; -import org.mozilla.gecko.util.FloatUtils; - import java.nio.ByteBuffer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -//import org.mozilla.gecko.GeckoApp; -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; /** * Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our * compositor. - *

+ * * TODO: Throttle down Gecko's priority when we pan and zoom. */ -public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventListener { +public class GeckoSoftwareLayerClient extends GeckoLayerClient { private static final String LOGTAG = "GeckoSoftwareLayerClient"; - private static final IntSize TILE_SIZE = new IntSize(256, 256); - private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L; - private static Pattern sColorPattern; - private Context mContext; + private int mFormat; - private IntSize mScreenSize; - private IntSize mBufferSize; + private IntSize mViewportSize; private ByteBuffer mBuffer; - private Layer mTileLayer; - /* The viewport rect that Gecko is currently displaying. */ - private ViewportMetrics mGeckoViewport; + private CairoImage mCairoImage; - private long mLastViewportChangeTime; - private boolean mPendingViewportAdjust; - private boolean mViewportSizeChanged; + + private static final IntSize TILE_SIZE = new IntSize(256, 256); + // Whether or not the last paint we got used direct texturing private boolean mHasDirectTexture; - // mUpdateViewportOnEndDraw is used to indicate that we received a - // viewport update notification while drawing. therefore, when the - // draw finishes, we need to update the entire viewport rather than - // just the page size. this boolean should always be accessed from - // inside a transaction, so no synchronization is needed. - private boolean mUpdateViewportOnEndDraw; public GeckoSoftwareLayerClient(Context context) { - mContext = context; + super(context); - mScreenSize = new IntSize(0, 0); - mBufferSize = new IntSize(0, 0); mFormat = CairoImage.FORMAT_RGB16_565; mCairoImage = new CairoImage() { @Override - public ByteBuffer getBuffer() { - return mBuffer; - } - + public ByteBuffer getBuffer() { return mBuffer; } @Override - public IntSize getSize() { - return mBufferSize; - } - + public IntSize getSize() { return mBufferSize; } @Override - public int getFormat() { - return mFormat; - } + public int getFormat() { return mFormat; } }; - - mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE); - } - - // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color - // cannot be parsed, returns white. - private static int parseColorFromGecko(String string) { - if (sColorPattern == null) { - sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); - } - - Matcher matcher = sColorPattern.matcher(string); - if (!matcher.matches()) { - return Color.WHITE; - } - - int r = Integer.parseInt(matcher.group(1)); - int g = Integer.parseInt(matcher.group(2)); - int b = Integer.parseInt(matcher.group(3)); - return Color.rgb(r, g, b); - } - - public int getWidth() { - return mBufferSize.width; - } - - public int getHeight() { - return mBufferSize.height; } protected void finalize() throws Throwable { try { if (mBuffer != null) - /*GeckoAppShell*/ LOKitShell.freeDirectBuffer(mBuffer); + LOKitShell.freeDirectBuffer(mBuffer); mBuffer = null; } finally { super.finalize(); } } - /** - * Attaches the root layer to the layer controller so that Gecko appears. - */ - @Override public void setLayerController(LayerController layerController) { super.setLayerController(layerController); @@ -172,142 +112,100 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL //GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this); //GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this); + //GeckoAppShell.registerGeckoEventListener("Checkerboard:Toggle", this); - // This needs to happen before a call to sendResizeEventIfNecessary - // happens, but only needs to be called once. As that is only called by - // the layer controller or this, here is a safe place to do so. - if (mTileLayer instanceof MultiTileLayer) { - //GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, TILE_SIZE); - //GeckoAppShell.sendEventToGecko(event); - LOKitShell.sendEvent(LOEvent.tileSize(TILE_SIZE)); - } - - sendResizeEventIfNecessary(); + // XXX: Review pcwalton. This signature changed on m-c, should force = false here? + sendResizeEventIfNecessary(false); } - private void setHasDirectTexture(boolean hasDirectTexture) { - if (hasDirectTexture == mHasDirectTexture) - return; + @Override + protected boolean handleDirectTextureChange(boolean hasDirectTexture) { + if (mTileLayer != null && hasDirectTexture == mHasDirectTexture) + return false; mHasDirectTexture = hasDirectTexture; - IntSize tileSize; if (mHasDirectTexture) { + Log.i(LOGTAG, "Creating WidgetTileLayer"); mTileLayer = new WidgetTileLayer(mCairoImage); - tileSize = new IntSize(0, 0); } else { + Log.i(LOGTAG, "Creating MultiTileLayer"); mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE); - tileSize = TILE_SIZE; } getLayerController().setRoot(mTileLayer); - //GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, tileSize); - //GeckoAppShell.sendEventToGecko(event); - LOKitShell.sendEvent(LOEvent.tileSize(tileSize)); - // Force a resize event to be sent because the results of this // are different depending on what tile system we're using sendResizeEventIfNecessary(true); - } - - public void beginDrawing(int width, int height) { - beginTransaction(mTileLayer); - - if (mBufferSize.width != width || mBufferSize.height != height) { - mBufferSize = new IntSize(width, height); - - // Reallocate the buffer if necessary - // * 2 because it's a 16-bit buffer (so 2 bytes per pixel). - int size = mBufferSize.getArea() * 2; - if (mBuffer == null || mBuffer.capacity() != size) { - // Free the old buffer - if (mBuffer != null) { - /*GeckoAppShell*/ - LOKitShell.freeDirectBuffer(mBuffer); - mBuffer = null; - } - - mBuffer = /*GeckoAppShell*/LOKitShell.allocateDirectBuffer(size); - } - } + return true; } - private void updateViewport(String viewportDescription, final boolean onlyUpdatePageSize) { - try { - JSONObject viewportObject = new JSONObject(viewportDescription); - - // save and restore the viewport size stored in java; never let the - // JS-side viewport dimensions override the java-side ones because - // java is the One True Source of this information, and allowing JS - // to override can lead to race conditions where this data gets clobbered. - FloatSize viewportSize = getLayerController().getViewportSize(); - mGeckoViewport = new ViewportMetrics(viewportObject); - mGeckoViewport.setSize(viewportSize); - - LayerController controller = getLayerController(); - PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin(); - mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); - mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); - - 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 { - Log.d(LOGTAG, "Received viewport update from gecko"); - controller.setViewportMetrics(mGeckoViewport); - controller.abortPanZoomAnimation(); + @Override + protected boolean shouldDrawProceed(int tileWidth, int tileHeight) { + // Make sure the tile-size matches. If it doesn't, we could crash trying + // to access invalid memory. + if (mHasDirectTexture) { + if (tileWidth != 0 || tileHeight != 0) { + Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + + tileHeight); + return false; } - - // Update the background color, if it's present. - String backgroundColorString = viewportObject.optString("backgroundColor"); - if (backgroundColorString != null) { - controller.setCheckerboardColor(parseColorFromGecko(backgroundColorString)); + } else { + if (tileWidth != TILE_SIZE.width || tileHeight != TILE_SIZE.height) { + Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + + tileHeight); + return false; } - } catch (JSONException e) { - Log.e(LOGTAG, "Bad viewport description: " + viewportDescription); - throw new RuntimeException(e); } - } - public ByteBuffer getBuffer() { - return mBuffer; + return true; } - /* - * TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require - * a little more JNI magic. - */ - public void endDrawing(int x, int y, int width, int height, String metadata, boolean hasDirectTexture) { - synchronized (getLayerController()) { - try { - updateViewport(metadata, !mUpdateViewportOnEndDraw); - mUpdateViewportOnEndDraw = false; - Rect rect = new Rect(x, y, x + width, y + height); + @Override + public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight, + String metadata, boolean hasDirectTexture) { + Rect bufferRect = super.beginDrawing(width, height, tileWidth, tileHeight, + metadata, hasDirectTexture); + if (bufferRect == null) { + return bufferRect; + } - setHasDirectTexture(hasDirectTexture); + // If the window size has changed, reallocate the buffer to match. + if (mBufferSize.width != width || mBufferSize.height != height) { + mBufferSize = new IntSize(width, height); - if (!mHasDirectTexture) - ((MultiTileLayer) mTileLayer).invalidate(rect); - } finally { - endTransaction(mTileLayer); + // Reallocate the buffer if necessary + if (mTileLayer instanceof MultiTileLayer) { + int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8; + int size = mBufferSize.getArea() * bpp; + if (mBuffer == null || mBuffer.capacity() != size) { + // Free the old buffer + if (mBuffer != null) { + LOKitShell.freeDirectBuffer(mBuffer); + mBuffer = null; + } + + mBuffer = LOKitShell.allocateDirectBuffer(size); + } } } + + return bufferRect; } - public ViewportMetrics getGeckoViewportMetrics() { - // Return a copy, as we modify this inside the Gecko thread - if (mGeckoViewport != null) - return new ViewportMetrics(mGeckoViewport); - return null; + @Override + protected void updateLayerAfterDraw(Rect updatedRect) { + if (!(mTileLayer instanceof MultiTileLayer)) { + return; + } + + ((MultiTileLayer)mTileLayer).invalidate(updatedRect); } - public void copyPixelsFromMultiTileLayer(Bitmap target) { - Canvas canvas = new Canvas(target); + private void copyPixelsFromMultiTileLayer(Bitmap target) { + Canvas c = new Canvas(target); ByteBuffer tileBuffer = mBuffer.slice(); int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8; @@ -323,7 +221,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer()); // Copy the tile to the master Bitmap and recycle it - canvas.drawBitmap(tile, x, y, null); + c.drawBitmap(tile, x, y, null); tile.recycle(); // Progress the buffer to the next tile @@ -333,7 +231,16 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } } + @Override + protected void tileLayerUpdated() { + /* No-op. */ + } + + @Override public Bitmap getBitmap() { + if (mTileLayer == null) + return null; + // Begin a tile transaction, otherwise the buffer can be destroyed while // we're reading from it. beginTransaction(mTileLayer); @@ -341,21 +248,17 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL if (mBuffer == null || mBufferSize.width <= 0 || mBufferSize.height <= 0) return null; try { - Bitmap bitmap = null; + Bitmap b = null; if (mTileLayer instanceof MultiTileLayer) { - bitmap = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height, + b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height, CairoUtils.cairoFormatTobitmapConfig(mFormat)); - copyPixelsFromMultiTileLayer(bitmap); - } else if (mTileLayer instanceof SingleTileLayer) { - bitmap = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height, - CairoUtils.cairoFormatTobitmapConfig(mFormat)); - bitmap.copyPixelsFromBuffer(mBuffer.asIntBuffer()); + copyPixelsFromMultiTileLayer(b); } else { Log.w(LOGTAG, "getBitmap() called on a layer (" + mTileLayer + ") we don't know how to get a bitmap from"); } - return bitmap; + return b; } catch (OutOfMemoryError oom) { Log.w(LOGTAG, "Unable to create bitmap", oom); return null; @@ -365,9 +268,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } } - /** - * Returns the back buffer. This function is for Gecko to use. - */ + /** Returns the back buffer. This function is for Gecko to use. */ public ByteBuffer lockBuffer() { return mBuffer; } @@ -381,125 +282,41 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } @Override - public void geometryChanged() { - /* Let Gecko know if the screensize has changed */ - sendResizeEventIfNecessary(); - render(); - } - - private void sendResizeEventIfNecessary() { - sendResizeEventIfNecessary(false); - } - - /* Informs Gecko that the screen size has changed. */ - private void sendResizeEventIfNecessary(boolean force) { - DisplayMetrics metrics = new DisplayMetrics(); - //GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - LibreOfficeMainActivity.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - - // Return immediately if the screen size hasn't changed or the viewport - // size is zero (which indicates that the rendering surface hasn't been - // allocated yet). - boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width || - metrics.heightPixels != mScreenSize.height); - boolean viewportSizeValid = (getLayerController() != null && - getLayerController().getViewportSize().isPositive()); - if (!(force || (screenSizeChanged && viewportSizeValid))) { - return; - } - - mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); - IntSize bufferSize; - - // Round up depending on layer implementation to remove texture wastage - if (mTileLayer instanceof MultiTileLayer) { - // Round to the next multiple of the tile size, respecting maximum texture size - bufferSize = new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) / TILE_SIZE.width + 1) * TILE_SIZE.width, - ((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) / TILE_SIZE.height + 1) * TILE_SIZE.height); - - } else { - int maxSize = getLayerController().getView().getMaxTextureSize(); - - // XXX Integrate gralloc/tiling work to circumvent this - if (mScreenSize.width > maxSize || mScreenSize.height > maxSize) - throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize); - - // Round to next power of two until we have NPOT texture support - bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)), - Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height))); - } - - Log.i(LOGTAG, "Screen-size changed to " + mScreenSize); - - /*GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED, - bufferSize.width, bufferSize.height, - metrics.widthPixels, metrics.heightPixels); - GeckoAppShell.sendEventToGecko(event);*/ - LOKitShell.sendEvent(LOEvent.sizeChanged(bufferSize.width, bufferSize.height, metrics.widthPixels, metrics.heightPixels)); - } - - @Override - public void viewportSizeChanged() { - mViewportSizeChanged = true; + public int getType() { + return LAYER_CLIENT_TYPE_SOFTWARE; } @Override - public void render() { - adjustViewportWithThrottling(); - } - - private void adjustViewportWithThrottling() { - if (!getLayerController().getRedrawHint()) - return; - - if (mPendingViewportAdjust) - return; - - long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime; - if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) { - getLayerController().getView().postDelayed( - new Runnable() { - public void run() { - mPendingViewportAdjust = false; - adjustViewport(); - } - }, MIN_VIEWPORT_CHANGE_DELAY - timeDelta - ); - mPendingViewportAdjust = true; - return; + protected IntSize getBufferSize() { + // Round up depending on layer implementation to remove texture wastage + if (!mHasDirectTexture) { + // Round to the next multiple of the tile size + return new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) / + TILE_SIZE.width + 1) * TILE_SIZE.width, + ((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) / + TILE_SIZE.height + 1) * TILE_SIZE.height); } - adjustViewport(); - } - - private void adjustViewport() { - ViewportMetrics viewportMetrics = new ViewportMetrics(getLayerController().getViewportMetrics()); + int maxSize = getLayerController().getView().getMaxTextureSize(); - PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(mBufferSize); - viewportMetrics.setViewportOffset(viewportOffset); - viewportMetrics.setViewport(viewportMetrics.getClampedViewport()); - - //GeckoAppShell.sendEventToGecko(new GeckoEvent(viewportMetrics)); - LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics)); - if (mViewportSizeChanged) { - mViewportSizeChanged = false; - //GeckoAppShell.viewSizeChanged(); + // XXX Integrate gralloc/tiling work to circumvent this + if (mScreenSize.width > maxSize || mScreenSize.height > maxSize) { + throw new RuntimeException("Screen size of " + mScreenSize + + " larger than maximum texture size of " + maxSize); } - mLastViewportChangeTime = System.currentTimeMillis(); + // Round to next power of two until we have NPOT texture support, respecting maximum + // texture size + return new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + + LayerController.MIN_BUFFER.width)), + Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + + LayerController.MIN_BUFFER.height))); } - public void handleMessage(String event, JSONObject message) { - if ("Viewport:UpdateAndDraw".equals(event)) { - mUpdateViewportOnEndDraw = true; - - // Redraw everything. - Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height); - //GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect)); - LOKitShell.sendEvent(LOEvent.draw(rect)); - } else if ("Viewport:UpdateLater".equals(event)) { - mUpdateViewportOnEndDraw = true; - } + @Override + protected IntSize getTileSize() { + // Round up depending on layer implementation to remove texture wastage + return !mHasDirectTexture ? TILE_SIZE : new IntSize(0, 0); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/Layer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/Layer.java index 17ecbba4058b..2dd3316f0016 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/Layer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/Layer.java @@ -21,6 +21,7 @@ * Contributor(s): * Patrick Walton * Chris Lord + * Arkady Blyakher * * 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 @@ -39,24 +40,23 @@ package org.mozilla.gecko.gfx; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Region; -import android.util.Log; -import java.util.concurrent.locks.ReentrantLock; -import javax.microedition.khronos.opengles.GL10; + import org.mozilla.gecko.util.FloatUtils; +import java.nio.FloatBuffer; +import java.util.concurrent.locks.ReentrantLock; + public abstract class Layer { private final ReentrantLock mTransactionLock; + protected Point mOrigin; + protected float mResolution; private boolean mInTransaction; private Point mNewOrigin; private float mNewResolution; private LayerView mView; - protected Point mOrigin; - protected float mResolution; - public Layer() { mTransactionLock = new ReentrantLock(); mOrigin = new Point(0, 0); @@ -67,7 +67,7 @@ public abstract class Layer { * Updates the layer. This returns false if there is still work to be done * after this update. */ - public final boolean update(GL10 gl, RenderContext context) { + public final boolean update(RenderContext context) { if (mTransactionLock.isHeldByCurrentThread()) { throw new RuntimeException("draw() called while transaction lock held by this " + "thread?!"); @@ -75,7 +75,7 @@ public abstract class Layer { if (mTransactionLock.tryLock()) { try { - return performUpdates(gl, context); + return performUpdates(context); } finally { mTransactionLock.unlock(); } @@ -84,13 +84,19 @@ public abstract class Layer { return false; } - /** Subclasses override this function to draw the layer. */ + /** + * Subclasses override this function to draw the layer. + */ public abstract void draw(RenderContext context); - /** Subclasses override this function to provide access to the size of the layer. */ + /** + * Subclasses override this function to provide access to the size of the layer. + */ public abstract IntSize getSize(); - /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */ + /** + * Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. + */ protected RectF getBounds(RenderContext context, FloatSize size) { float scaleFactor = context.zoomFactor / mResolution; float x = mOrigin.x * scaleFactor, y = mOrigin.y * scaleFactor; @@ -111,7 +117,7 @@ public abstract class Layer { * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer" * includes altering the underlying CairoImage in any way. Thus you must call this function * before modifying the byte buffer associated with this layer. - * + *

* This function may block, so you should never call this on the main UI thread. */ public void beginTransaction(LayerView aView) { @@ -127,7 +133,9 @@ public abstract class Layer { beginTransaction(null); } - /** Call this when you're done modifying the layer. */ + /** + * Call this when you're done modifying the layer. + */ public void endTransaction() { if (!mInTransaction) throw new RuntimeException("endTransaction() called outside a transaction"); @@ -138,24 +146,32 @@ public abstract class Layer { mView.requestRender(); } - /** Returns true if the layer is currently in a transaction and false otherwise. */ + /** + * Returns true if the layer is currently in a transaction and false otherwise. + */ protected boolean inTransaction() { return mInTransaction; } - /** Returns the current layer origin. */ + /** + * Returns the current layer origin. + */ public Point getOrigin() { return mOrigin; } - /** Sets the origin. Only valid inside a transaction. */ + /** + * Sets the origin. Only valid inside a transaction. + */ public void setOrigin(Point newOrigin) { if (!mInTransaction) throw new RuntimeException("setOrigin() is only valid inside a transaction"); mNewOrigin = newOrigin; } - /** Returns the current layer's resolution. */ + /** + * Returns the current layer's resolution. + */ public float getResolution() { return mResolution; } @@ -164,7 +180,8 @@ public abstract class Layer { * 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. */ + * Only valid inside a transaction. + */ public void setResolution(float newResolution) { if (!mInTransaction) throw new RuntimeException("setResolution() is only valid inside a transaction"); @@ -177,7 +194,7 @@ public abstract class Layer { * superclass implementation. Returns false if there is still work to be done after this * update is complete. */ - protected boolean performUpdates(GL10 gl, RenderContext context) { + protected boolean performUpdates(RenderContext context) { if (mNewOrigin != null) { mOrigin = mNewOrigin; mNewOrigin = null; @@ -190,15 +207,26 @@ public abstract class Layer { return true; } + protected boolean dimensionChangesPending() { + return (mNewOrigin != null) || (mNewResolution != 0.0f); + } + public static class RenderContext { public final RectF viewport; public final FloatSize pageSize; public final float zoomFactor; + public final int positionHandle; + public final int textureHandle; + public final FloatBuffer coordBuffer; - public RenderContext(RectF aViewport, FloatSize aPageSize, float aZoomFactor) { + public RenderContext(RectF aViewport, FloatSize aPageSize, float aZoomFactor, + int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) { viewport = aViewport; pageSize = aPageSize; zoomFactor = aZoomFactor; + positionHandle = aPositionHandle; + textureHandle = aTextureHandle; + coordBuffer = aCoordBuffer; } public boolean fuzzyEquals(RenderContext other) { @@ -211,3 +239,4 @@ public abstract class Layer { } } } + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerController.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerController.java index fb0823823877..250dc84c69fe 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerController.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerController.java @@ -49,7 +49,6 @@ import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; import org.mozilla.gecko.ui.PanZoomController; import org.mozilla.gecko.ui.SimpleScaleGestureDetector; @@ -57,11 +56,6 @@ import org.mozilla.gecko.ui.SimpleScaleGestureDetector; import java.util.Timer; import java.util.TimerTask; -//import org.mozilla.gecko.GeckoApp; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.Tabs; -//import org.mozilla.gecko.Tab; - /** * 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 @@ -69,7 +63,7 @@ import java.util.TimerTask; *

* Many methods require that the monitor be held, with a synchronized (controller) { ... } block. */ -public class LayerController /*implements Tabs.OnTabsChangedListener*/ { +public class LayerController { /* 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. @@ -80,13 +74,16 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { * 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 static final int PREVENT_DEFAULT_TIMEOUT = 200; private Layer mRootLayer; /* The root layer. */ private LayerView mView; /* The main rendering view. */ - private Context mContext; /* The current context. */ /* * The panning and zooming controller, which interprets pan and zoom gestures for us and * updates our visible rect appropriately. */ + private Context mContext; /* The current context. */ private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */ private boolean mWaitForTouchListeners; private PanZoomController mPanZoomController; @@ -96,12 +93,9 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { private int mCheckerboardColor; private boolean mCheckerboardShouldShowChecks; private boolean mForceRedraw; - /* 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 boolean inTouchSession = false; private PointF initialTouchLocation = null; public LayerController(Context context) { @@ -111,15 +105,6 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { mViewportMetrics = new ViewportMetrics(); mPanZoomController = new PanZoomController(this); mView = new LayerView(context, this); - - //Tabs.getInstance().registerOnTabsChangedListener(this); - - ViewConfiguration vc = ViewConfiguration.get(mContext); - mTimeout = vc.getLongPressTimeout(); - } - - public void onDestroy() { - //Tabs.getInstance().unregisterOnTabsChangedListener(this); } public void setForceRedraw() { @@ -364,12 +349,15 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { * would prefer that the action didn't take place. */ public boolean getRedrawHint() { + // FIXME: Allow redraw while a finger is down, but only if we're about to checkerboard. + // This requires fixing aboutToCheckerboard() to know about the new buffer size. + if (mForceRedraw) { mForceRedraw = false; return true; } - return aboutToCheckerboard() && mPanZoomController.getRedrawHint(); + return mPanZoomController.getRedrawHint(); } private RectF getTileRect() { @@ -426,29 +414,15 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { 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) { + mView.clearEventQueue(); 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() { + post(new Runnable() { public void run() { - post(new Runnable() { - public void run() { - preventPanning(false); - } - }); + preventPanning(mWaitForTouchListeners); } - }, mTimeout); + }); } // After the initial touch, ignore touch moves until they exceed a minimum distance. @@ -460,16 +434,55 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { } } - // send the event to content if (mOnTouchListener != null) mOnTouchListener.onTouch(mView, event); + if (!mWaitForTouchListeners) + return !allowDefaultActions; + + boolean createTimer = false; + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { + if (!inTouchSession && allowDefaultTimer == null) { + inTouchSession = true; + createTimer = true; + } + break; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + // if we still have initialTouchLocation, we haven't fired any + // touchmove events. We should start the timer to wait for preventDefault + // from touchstart. If we don't hear from it we fire mouse events + if (initialTouchLocation != null) + createTimer = true; + inTouchSession = false; + } + } + + if (createTimer) { + if (allowDefaultTimer != null) { + allowDefaultTimer.cancel(); + } + allowDefaultTimer = new Timer(); + allowDefaultTimer.schedule(new TimerTask() { + public void run() { + post(new Runnable() { + public void run() { + preventPanning(false); + } + }); + } + }, PREVENT_DEFAULT_TIMEOUT); + } + return !allowDefaultActions; } public void preventPanning(boolean aValue) { if (allowDefaultTimer != null) { allowDefaultTimer.cancel(); + allowDefaultTimer.purge(); allowDefaultTimer = null; } if (aValue == allowDefaultActions) { @@ -484,11 +497,6 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { } } - /*public void onTabChanged(Tab tab, Tabs.TabEvents msg) { - if ((Tabs.getInstance().isSelectedTab(tab) && msg == Tabs.TabEvents.STOP) || msg == Tabs.TabEvents.SELECTED) { - mWaitForTouchListeners = tab.getHasTouchListeners(); - } - }*/ public void setWaitForTouchListeners(boolean aValue) { mWaitForTouchListeners = aValue; } @@ -523,3 +531,4 @@ public class LayerController /*implements Tabs.OnTabsChangedListener*/ { mView.requestRender(); } } + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java index 9081a19e4ecb..6690aad8ec16 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java @@ -21,6 +21,7 @@ * Contributor(s): * Patrick Walton * Chris Lord + * Arkady Blyakher * * 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 @@ -38,17 +39,18 @@ package org.mozilla.gecko.gfx; +import org.libreoffice.LOKitShell; import org.mozilla.gecko.gfx.BufferedCairoImage; import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.Layer.RenderContext; import org.mozilla.gecko.gfx.LayerController; -import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.NinePatchTileLayer; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.gfx.TextureReaper; import org.mozilla.gecko.gfx.TextureGenerator; import org.mozilla.gecko.gfx.TextLayer; import org.mozilla.gecko.gfx.TileLayer; +//import org.mozilla.gecko.GeckoAppShell; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Point; @@ -57,6 +59,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.RegionIterator; +import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -64,6 +67,9 @@ import android.util.Log; import android.view.WindowManager; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; @@ -92,6 +98,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final ScrollbarLayer mHorizScrollLayer; private final ScrollbarLayer mVertScrollLayer; private final FadeRunnable mFadeRunnable; + private final FloatBuffer mCoordBuffer; private RenderContext mLastPageContext; private int mMaxTextureSize; @@ -111,6 +118,48 @@ public class LayerRenderer implements GLSurfaceView.Renderer { /* Used by robocop for testing purposes */ private IntBuffer mPixelBuffer; + // Used by GLES 2.0 + private int mProgram; + private int mPositionHandle; + private int mTextureHandle; + private int mSampleHandle; + private int mTMatrixHandle; + + // column-major matrix applied to each vertex to shift the viewport from + // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by + // a factor of 2 to fill up the screen + private static final float[] TEXTURE_MATRIX = { + 2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 2.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f + }; + + private static final int COORD_BUFFER_SIZE = 20; + + // The shaders run on the GPU directly, the vertex shader is only applying the + // matrix transform detailed above + private static final String VERTEX_SHADER = + "uniform mat4 uTMatrix;\n" + + "attribute vec4 vPosition;\n" + + "attribute vec2 aTexCoord;\n" + + "varying vec2 vTexCoord;\n" + + "void main() {\n" + + " gl_Position = uTMatrix * vPosition;\n" + + " vTexCoord = aTexCoord;\n" + + "}\n"; + + // Note we flip the y-coordinate in the fragment shader from a + // coordinate system with (0,0) in the top left to one with (0,0) in + // the bottom left. + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "varying vec2 vTexCoord;\n" + + "uniform sampler2D sTexture;\n" + + "void main() {\n" + + " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" + + "}\n"; + public LayerRenderer(LayerView view) { mView = view; @@ -135,20 +184,69 @@ public class LayerRenderer implements GLSurfaceView.Renderer { mFrameTimings = new int[60]; mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0; mShowFrameRate = false; + + // Initialize the FloatBuffer that will be used to store all vertices and texture + // coordinates in draw() commands. + ByteBuffer byteBuffer = LOKitShell.allocateDirectBuffer(COORD_BUFFER_SIZE * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + mCoordBuffer = byteBuffer.asFloatBuffer(); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { checkMonitoringEnabled(); + createProgram(); + activateProgram(); + } + + public void createProgram() { + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); - gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); - gl.glDisable(GL10.GL_DITHER); - gl.glEnable(GL10.GL_TEXTURE_2D); + mProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program + GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program + GLES20.glLinkProgram(mProgram); // creates OpenGL program executables + + // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members. + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); + mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); + mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture"); + mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix"); int maxTextureSizeResult[] = new int[1]; - gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); + GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); mMaxTextureSize = maxTextureSizeResult[0]; + } + + // Activates the shader program. + public void activateProgram() { + // Add the program to the OpenGL environment + GLES20.glUseProgram(mProgram); + + // Set the transformation matrix + GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, TEXTURE_MATRIX, 0); + + Log.e(LOGTAG, "### Position handle is " + mPositionHandle + ", texture handle is " + + mTextureHandle + ", last error is " + GLES20.glGetError()); + + // Enable the arrays from which we get the vertex and texture coordinates + GLES20.glEnableVertexAttribArray(mPositionHandle); + GLES20.glEnableVertexAttribArray(mTextureHandle); + + GLES20.glUniform1i(mSampleHandle, 0); TextureGenerator.get().fill(); + + // TODO: Move these calls into a separate deactivate() call that is called after the + // underlay and overlay are rendered. + } + + // Deactivates the shader program. This must be done to avoid crashes after returning to the + // Gecko C++ compositor from Java. + public void deactivateProgram() { + GLES20.glDisableVertexAttribArray(mTextureHandle); + GLES20.glDisableVertexAttribArray(mPositionHandle); + GLES20.glUseProgram(0); } public int getMaxTextureSize() { @@ -179,149 +277,14 @@ public class LayerRenderer implements GLSurfaceView.Renderer { * Called whenever a new frame is about to be drawn. */ public void onDrawFrame(GL10 gl) { - long frameStartTime = SystemClock.uptimeMillis(); - - TextureReaper.get().reap(gl); - TextureGenerator.get().fill(); - - LayerController controller = mView.getController(); - RenderContext screenContext = createScreenContext(); - - boolean updated = true; - - 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) updated &= rootLayer.update(gl, pageContext); - updated &= mBackgroundLayer.update(gl, screenContext); - updated &= mShadowLayer.update(gl, pageContext); - updateCheckerboardLayer(gl, screenContext); - updated &= mFrameRateLayer.update(gl, screenContext); - updated &= mVertScrollLayer.update(gl, pageContext); - updated &= mHorizScrollLayer.update(gl, pageContext); - - for (Layer layer : mExtraLayers) - updated &= layer.update(gl, pageContext); - - /* Draw the background. */ - mBackgroundLayer.draw(screenContext); - - /* 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 any extra layers that were added (likely plugins) */ - for (Layer layer : mExtraLayers) - layer.draw(pageContext); - - /* 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); - - /* Measure how much of the screen is checkerboarding */ - if ((rootLayer != null) && - (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) { - // Find out how much of the viewport area is valid - Rect viewport = RectUtils.round(pageContext.viewport); - Region validRegion = rootLayer.getValidRegion(pageContext); - validRegion.op(viewport, Region.Op.INTERSECT); - - float checkerboard = 0.0f; - if (!(validRegion.isRect() && validRegion.getBounds().equals(viewport))) { - int screenArea = viewport.width() * viewport.height(); - validRegion.op(viewport, Region.Op.REVERSE_DIFFERENCE); - - // XXX The assumption here is that a Region never has overlapping - // rects. This is true, as evidenced by reading the SkRegion - // source, but is not mentioned in the Android documentation, - // and so is liable to change. - // If it does change, this code will need to be reevaluated. - Rect r = new Rect(); - int checkerboardArea = 0; - for (RegionIterator i = new RegionIterator(validRegion); i.next(r);) { - checkerboardArea += r.width() * r.height(); - } - - checkerboard = checkerboardArea / (float)screenArea; - } - - PanningPerfAPI.recordCheckerboard(checkerboard); - - mCompleteFramesRendered += 1.0f - checkerboard; - mFramesRendered ++; - - if (frameStartTime - mProfileOutputTime > 1000) { - mProfileOutputTime = frameStartTime; - printCheckerboardStats(); - } - } - } - - /* Draw the FPS. */ - if (mShowFrameRate) { - updateDroppedFrames(frameStartTime); - - try { - gl.glEnable(GL10.GL_BLEND); - gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); - mFrameRateLayer.draw(screenContext); - } finally { - gl.glDisable(GL10.GL_BLEND); - } - } - - // If a layer update requires further work, schedule another redraw - if (!updated) - mView.requestRender(); - - PanningPerfAPI.recordFrameTime(); - - /* Used by robocop for testing purposes */ - IntBuffer pixelBuffer = mPixelBuffer; - if (updated && pixelBuffer != null) { - synchronized (pixelBuffer) { - pixelBuffer.position(0); - gl.glReadPixels(0, 0, (int)screenContext.viewport.width(), (int)screenContext.viewport.height(), GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer); - pixelBuffer.notify(); - } + RenderContext pageContext = createPageContext(), screenContext = createScreenContext(); + Frame frame = createFrame(pageContext, screenContext); + synchronized (mView.getController()) { + frame.beginDrawing(); + frame.drawBackground(); + frame.drawRootLayer(); + frame.drawForeground(); + frame.endDrawing(); } } @@ -346,15 +309,15 @@ public class LayerRenderer implements GLSurfaceView.Renderer { return pixelBuffer; } - private RenderContext createScreenContext() { + public RenderContext createScreenContext() { LayerController layerController = mView.getController(); IntSize viewportSize = new IntSize(layerController.getViewportSize()); RectF viewport = new RectF(0.0f, 0.0f, viewportSize.width, viewportSize.height); FloatSize pageSize = new FloatSize(layerController.getPageSize()); - return new RenderContext(viewport, pageSize, 1.0f); + return createContext(viewport, pageSize, 1.0f); } - private RenderContext createPageContext() { + public RenderContext createPageContext() { LayerController layerController = mView.getController(); Rect viewport = new Rect(); @@ -362,7 +325,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer { FloatSize pageSize = new FloatSize(layerController.getPageSize()); float zoomFactor = layerController.getZoomFactor(); - return new RenderContext(new RectF(viewport), pageSize, zoomFactor); + return createContext(new RectF(viewport), pageSize, zoomFactor); + } + + private RenderContext createContext(RectF viewport, FloatSize pageSize, float zoomFactor) { + return new RenderContext(viewport, pageSize, zoomFactor, mPositionHandle, mTextureHandle, + mCoordBuffer); } private Rect getPageRect() { @@ -374,7 +342,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { origin.negate(); return new Rect(origin.x, origin.y, - origin.x + pageSize.width, origin.y + pageSize.height); + origin.x + pageSize.width, origin.y + pageSize.height); } private Rect transformToScissorRect(Rect rect) { @@ -387,11 +355,11 @@ public class LayerRenderer implements GLSurfaceView.Renderer { int bottom = Math.min(screenSize.height, rect.bottom); return new Rect(left, screenSize.height - bottom, right, - (screenSize.height - bottom) + (bottom - top)); + (screenSize.height - bottom) + (bottom - top)); } public void onSurfaceChanged(GL10 gl, final int width, final int height) { - gl.glViewport(0, 0, width, height); + GLES20.glViewport(0, 0, width, height); // updating the state in the view/controller/client should be // done on the main UI thread, not the GL renderer thread @@ -431,7 +399,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { mFrameRateLayer.beginTransaction(); try { Point origin = new Point(width - FRAME_RATE_METER_WIDTH - 8, - height - FRAME_RATE_METER_HEIGHT + 8); + height - FRAME_RATE_METER_HEIGHT + 8); mFrameRateLayer.setOrigin(origin); } finally { mFrameRateLayer.endTransaction(); @@ -451,11 +419,11 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } - private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) { + private void updateCheckerboardLayer(RenderContext renderContext) { int checkerboardColor = mView.getController().getCheckerboardColor(); boolean showChecks = mView.getController().checkerboardShouldShowChecks(); if (checkerboardColor == mCheckerboardImage.getColor() && - showChecks == mCheckerboardImage.getShowChecks()) { + showChecks == mCheckerboardImage.getShowChecks()) { return; } @@ -467,7 +435,22 @@ public class LayerRenderer implements GLSurfaceView.Renderer { mCheckerboardLayer.endTransaction(); } - mCheckerboardLayer.update(gl, renderContext); + mCheckerboardLayer.update(renderContext); + } + + /* + * create a vertex shader type (GLES20.GL_VERTEX_SHADER) + * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) + */ + private int loadShader(int type, String shaderCode) { + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + return shader; + } + + public Frame createFrame(RenderContext pageContext, RenderContext screenContext) { + return new Frame(pageContext, screenContext); } class FadeRunnable implements Runnable { @@ -505,4 +488,202 @@ public class LayerRenderer implements GLSurfaceView.Renderer { } } } + + public class Frame { + // The timestamp recording the start of this frame. + private long mFrameStartTime; + // A rendering context for page-positioned layers, and one for screen-positioned layers. + private RenderContext mPageContext, mScreenContext; + // Whether a layer was updated. + private boolean mUpdated; + + public Frame(RenderContext pageContext, RenderContext screenContext) { + mPageContext = pageContext; + mScreenContext = screenContext; + } + + private void setScissorRect() { + Rect scissorRect = transformToScissorRect(getPageRect()); + GLES20.glEnable(GLES20.GL_SCISSOR_TEST); + GLES20.glScissor(scissorRect.left, scissorRect.top, + scissorRect.width(), scissorRect.height()); + } + + public void beginDrawing() { + mFrameStartTime = SystemClock.uptimeMillis(); + + TextureReaper.get().reap(); + TextureGenerator.get().fill(); + + mUpdated = true; + + LayerController controller = mView.getController(); + Layer rootLayer = controller.getRoot(); + + if (!mPageContext.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 = mPageContext; + + /* Update layers. */ + if (rootLayer != null) mUpdated &= rootLayer.update(mPageContext); + mUpdated &= mBackgroundLayer.update(mScreenContext); + mUpdated &= mShadowLayer.update(mPageContext); + updateCheckerboardLayer(mScreenContext); + mUpdated &= mFrameRateLayer.update(mScreenContext); + mUpdated &= mVertScrollLayer.update(mPageContext); + mUpdated &= mHorizScrollLayer.update(mPageContext); + + for (Layer layer : mExtraLayers) + mUpdated &= layer.update(mPageContext); + + GLES20.glDisable(GLES20.GL_SCISSOR_TEST); + + // If a layer update requires further work, schedule another redraw + if (!mUpdated) + mView.requestRender(); + + PanningPerfAPI.recordFrameTime(); + + /* Used by robocop for testing purposes */ + IntBuffer pixelBuffer = mPixelBuffer; + if (mUpdated && pixelBuffer != null) { + synchronized (pixelBuffer) { + pixelBuffer.position(0); + GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(), + (int)mScreenContext.viewport.height(), GLES20.GL_RGBA, + GLES20.GL_UNSIGNED_BYTE, pixelBuffer); + pixelBuffer.notify(); + } + } + } + + public void drawBackground() { + /* Draw the background. */ + mBackgroundLayer.draw(mScreenContext); + + /* 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(mView.getController().getViewport())) + mShadowLayer.draw(mPageContext); + + /* Draw the checkerboard. */ + setScissorRect(); + mCheckerboardLayer.draw(mScreenContext); + GLES20.glDisable(GLES20.GL_SCISSOR_TEST); + } + + // Draws the layer the client added to us. + void drawRootLayer() { + Layer rootLayer = mView.getController().getRoot(); + if (rootLayer == null) { + return; + } + + setScissorRect(); + rootLayer.draw(mPageContext); + GLES20.glDisable(GLES20.GL_SCISSOR_TEST); + } + + public void drawForeground() { + Rect pageRect = getPageRect(); + LayerController controller = mView.getController(); + + /* Draw any extra layers that were added (likely plugins) */ + for (Layer layer : mExtraLayers) + layer.draw(mPageContext); + + /* Draw the vertical scrollbar. */ + IntSize screenSize = new IntSize(controller.getViewportSize()); + if (pageRect.height() > screenSize.height) + mVertScrollLayer.draw(mPageContext); + + /* Draw the horizontal scrollbar. */ + if (pageRect.width() > screenSize.width) + mHorizScrollLayer.draw(mPageContext); + + /* Measure how much of the screen is checkerboarding */ + Layer rootLayer = controller.getRoot(); + if ((rootLayer != null) && + (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) { + // Find out how much of the viewport area is valid + Rect viewport = RectUtils.round(mPageContext.viewport); + Region validRegion = rootLayer.getValidRegion(mPageContext); + validRegion.op(viewport, Region.Op.INTERSECT); + + float checkerboard = 0.0f; + if (!(validRegion.isRect() && validRegion.getBounds().equals(viewport))) { + int screenArea = viewport.width() * viewport.height(); + validRegion.op(viewport, Region.Op.REVERSE_DIFFERENCE); + + // XXX The assumption here is that a Region never has overlapping + // rects. This is true, as evidenced by reading the SkRegion + // source, but is not mentioned in the Android documentation, + // and so is liable to change. + // If it does change, this code will need to be reevaluated. + Rect r = new Rect(); + int checkerboardArea = 0; + for (RegionIterator i = new RegionIterator(validRegion); i.next(r);) { + checkerboardArea += r.width() * r.height(); + } + + checkerboard = checkerboardArea / (float)screenArea; + } + + PanningPerfAPI.recordCheckerboard(checkerboard); + + mCompleteFramesRendered += 1.0f - checkerboard; + mFramesRendered ++; + + if (mFrameStartTime - mProfileOutputTime > 1000) { + mProfileOutputTime = mFrameStartTime; + printCheckerboardStats(); + } + } + + /* Draw the FPS. */ + if (mShowFrameRate) { + updateDroppedFrames(mFrameStartTime); + + try { + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + mFrameRateLayer.draw(mScreenContext); + } finally { + GLES20.glDisable(GLES20.GL_BLEND); + } + } + } + + public void endDrawing() { + // If a layer update requires further work, schedule another redraw + if (!mUpdated) + mView.requestRender(); + + PanningPerfAPI.recordFrameTime(); + + /* Used by robocop for testing purposes */ + IntBuffer pixelBuffer = mPixelBuffer; + if (mUpdated && pixelBuffer != null) { + synchronized (pixelBuffer) { + pixelBuffer.position(0); + GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(), + (int)mScreenContext.viewport.height(), GLES20.GL_RGBA, + GLES20.GL_UNSIGNED_BYTE, pixelBuffer); + pixelBuffer.notify(); + } + } + } + } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java index 2b63f1df15ea..a3c04fa600f2 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Arkady Blyakher * * 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 @@ -37,18 +38,23 @@ package org.mozilla.gecko.gfx; +//import org.mozilla.gecko.GeckoInputConnection; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.InputConnectionHandler; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.ui.SimpleScaleGestureDetector; import android.content.Context; import android.opengl.GLSurfaceView; +import android.view.View; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.ScaleGestureDetector; +import android.widget.RelativeLayout; import android.util.Log; +import java.nio.IntBuffer; import java.util.LinkedList; /** @@ -57,7 +63,7 @@ import java.util.LinkedList; * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a * mediator between the LayerRenderer and the LayerController. */ -public class LayerView extends GLSurfaceView { +public class LayerView extends FlexibleGLSurfaceView { private Context mContext; private LayerController mController; private InputConnectionHandler mInputConnectionHandler; @@ -70,6 +76,7 @@ public class LayerView extends GLSurfaceView { /* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */ private LinkedList mEventQueue = new LinkedList(); + public LayerView(Context context, LayerController controller) { super(context); @@ -77,15 +84,16 @@ public class LayerView extends GLSurfaceView { mController = controller; mRenderer = new LayerRenderer(this); setRenderer(mRenderer); - setRenderMode(RENDERMODE_WHEN_DIRTY); mGestureDetector = new GestureDetector(context, controller.getGestureListener()); mScaleGestureDetector = - new SimpleScaleGestureDetector(controller.getScaleGestureListener()); + new SimpleScaleGestureDetector(controller.getScaleGestureListener()); mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener()); mInputConnectionHandler = null; setFocusable(true); setFocusableInTouchMode(true); + + createGLThread(); } private void addToEventQueue(MotionEvent event) { @@ -131,9 +139,11 @@ public class LayerView extends GLSurfaceView { mController.setViewportSize(new FloatSize(size)); } - public void setInputConnectionHandler(InputConnectionHandler handler) { - mInputConnectionHandler = handler; - } + //public GeckoInputConnection setInputConnectionHandler() { + // GeckoInputConnection geckoInputConnection = GeckoInputConnection.create(this); + // mInputConnectionHandler = geckoInputConnection; + // return geckoInputConnection; + //} @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { @@ -211,5 +221,10 @@ public class LayerView extends GLSurfaceView { public int getMaxTextureSize() { return mRenderer.getMaxTextureSize(); } + + /** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */ + public IntBuffer getPixels() { + return mRenderer.getPixels(); + } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/MultiTileLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/MultiTileLayer.java index 94a605f35572..3ade6c14d2d6 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/MultiTileLayer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/MultiTileLayer.java @@ -1,39 +1,40 @@ /* -*- 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-2012 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Chris Lord - * - * 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 ***** */ +* ***** 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-2012 +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Chris Lord +* Arkady Blyakher +* +* 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; @@ -43,10 +44,11 @@ import org.mozilla.gecko.gfx.SingleTileLayer; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Region; import android.util.Log; import java.nio.ByteBuffer; +import java.nio.FloatBuffer; import java.util.ArrayList; -import javax.microedition.khronos.opengles.GL10; /** * Encapsulates the logic needed to draw a layer made of multiple tiles. @@ -59,7 +61,7 @@ public class MultiTileLayer extends Layer { private final CairoImage mImage; private IntSize mTileSize; private IntSize mBufferSize; - private final ArrayList mTiles; + private final ArrayList mTiles; public MultiTileLayer(CairoImage image, IntSize tileSize) { super(); @@ -67,34 +69,29 @@ public class MultiTileLayer extends Layer { mImage = image; mTileSize = tileSize; mBufferSize = new IntSize(0, 0); - mTiles = new ArrayList(); + mTiles = new ArrayList(); } public void invalidate(Rect dirtyRect) { - if (!inTransaction()) + if (!inTransaction()) { throw new RuntimeException("invalidate() is only valid inside a transaction"); + } - int x = 0, y = 0; - IntSize size = getSize(); - for (SingleTileLayer layer : mTiles) { - Rect tileRect = new Rect(x, y, x + mTileSize.width, y + mTileSize.height); + for (SubTile layer : mTiles) { + IntSize tileSize = layer.getSize(); + Rect tileRect = new Rect(layer.x, layer.y, layer.x + tileSize.width, layer.y + tileSize.height); if (tileRect.intersect(dirtyRect)) { - tileRect.offset(-x, -y); + tileRect.offset(-layer.x, -layer.y); layer.invalidate(tileRect); } - - x += mTileSize.width; - if (x >= size.width) { - x = 0; - y += mTileSize.height; - } } } public void invalidate() { - for (SingleTileLayer layer : mTiles) + for (SubTile layer : mTiles) { layer.invalidate(); + } } @Override @@ -105,8 +102,9 @@ public class MultiTileLayer extends Layer { private void validateTiles() { IntSize size = getSize(); - if (size.equals(mBufferSize)) + if (size.equals(mBufferSize)) { return; + } // Regenerate tiles mTiles.clear(); @@ -120,7 +118,7 @@ public class MultiTileLayer extends Layer { // tile from the parent CairoImage. It's assumed that // the tiles are stored in series. final IntSize layerSize = - new IntSize(Math.min(mTileSize.width, size.width - x), + new IntSize(Math.min(mTileSize.width, size.width - x), Math.min(mTileSize.height, size.height - y)); final int tileOffset = offset; @@ -148,7 +146,7 @@ public class MultiTileLayer extends Layer { } }; - mTiles.add(new SingleTileLayer(subImage)); + mTiles.add(new SubTile(subImage, x, y)); offset += layerSize.getArea() * bpp; } } @@ -160,21 +158,21 @@ public class MultiTileLayer extends Layer { } @Override - protected boolean performUpdates(GL10 gl, RenderContext context) { - super.performUpdates(gl, context); + protected boolean performUpdates(RenderContext context) { + super.performUpdates(context); validateTiles(); // Iterate over the tiles and decide which ones we'll be drawing int dirtyTiles = 0; boolean screenUpdateDone = false; - SingleTileLayer firstDirtyTile = null; - for (SingleTileLayer layer : mTiles) { + SubTile firstDirtyTile = null; + for (SubTile layer : mTiles) { // First do a non-texture update to make sure coordinates are // up-to-date. boolean invalid = layer.getSkipTextureUpdate(); layer.setSkipTextureUpdate(true); - layer.performUpdates(gl, context); + layer.performUpdates(context); RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize())); boolean isDirty = layer.isDirty(); @@ -190,7 +188,7 @@ public class MultiTileLayer extends Layer { // update it immediately. layer.setSkipTextureUpdate(false); screenUpdateDone = true; - layer.performUpdates(gl, context); + layer.performUpdates(context); invalid = false; } } @@ -208,7 +206,7 @@ public class MultiTileLayer extends Layer { // upload-related hitches. if (!screenUpdateDone && firstDirtyTile != null) { firstDirtyTile.setSkipTextureUpdate(false); - firstDirtyTile.performUpdates(gl, context); + firstDirtyTile.performUpdates(context); dirtyTiles --; } @@ -216,24 +214,21 @@ public class MultiTileLayer extends Layer { } private void refreshTileMetrics(Point origin, float resolution, boolean inTransaction) { - int x = 0, y = 0; IntSize size = getSize(); - for (SingleTileLayer layer : mTiles) { - if (!inTransaction) + for (SubTile layer : mTiles) { + if (!inTransaction) { layer.beginTransaction(null); + } - if (origin != null) - layer.setOrigin(new Point(origin.x + x, origin.y + y)); - if (resolution >= 0.0f) + if (origin != null) { + layer.setOrigin(new Point(origin.x + layer.x, origin.y + layer.y)); + } + if (resolution >= 0.0f) { layer.setResolution(resolution); + } - if (!inTransaction) + if (!inTransaction) { layer.endTransaction(); - - x += mTileSize.width; - if (x >= size.width) { - x = 0; - y += mTileSize.height; } } } @@ -254,21 +249,23 @@ public class MultiTileLayer extends Layer { public void beginTransaction(LayerView aView) { super.beginTransaction(aView); - for (SingleTileLayer layer : mTiles) + for (SubTile layer : mTiles) { layer.beginTransaction(aView); + } } @Override public void endTransaction() { - for (SingleTileLayer layer : mTiles) + for (SubTile layer : mTiles) { layer.endTransaction(); + } super.endTransaction(); } @Override public void draw(RenderContext context) { - for (SingleTileLayer layer : mTiles) { + for (SubTile layer : mTiles) { // We use the SkipTextureUpdate flag as a validity flag. If it's false, // the contents of this tile are invalid and we shouldn't draw it. if (layer.getSkipTextureUpdate()) @@ -280,5 +277,28 @@ public class MultiTileLayer extends Layer { layer.draw(context); } } + + @Override + public Region getValidRegion(RenderContext context) { + Region validRegion = new Region(); + for (SubTile tile : mTiles) { + if (tile.getSkipTextureUpdate()) + continue; + validRegion.op(tile.getValidRegion(context), Region.Op.UNION); + } + + return validRegion; + } + + class SubTile extends SingleTileLayer { + public int x; + public int y; + + public SubTile(CairoImage mImage, int mX, int mY) { + super(mImage); + x = mX; + y = mY; + } + } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java index ffc87b3dcc2a..9a5049781c45 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java @@ -38,6 +38,8 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.util.FloatUtils; +import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import org.json.JSONException; @@ -60,27 +62,27 @@ public final class RectUtils { float halfLessWidth = lessWidth / 2.0f; float halfLessHeight = lessHeight / 2.0f; return new Rect(Math.round(rect.left + halfLessWidth), - Math.round(rect.top + halfLessHeight), - Math.round(rect.right - halfLessWidth), - Math.round(rect.bottom - halfLessHeight)); + Math.round(rect.top + halfLessHeight), + Math.round(rect.right - halfLessWidth), + Math.round(rect.bottom - halfLessHeight)); } public static RectF contract(RectF rect, float lessWidth, float lessHeight) { float halfLessWidth = lessWidth / 2; float halfLessHeight = lessHeight / 2; return new RectF(rect.left + halfLessWidth, - rect.top + halfLessHeight, - rect.right - halfLessWidth, - rect.bottom - halfLessHeight); + rect.top + halfLessHeight, + rect.right - halfLessWidth, + rect.bottom - halfLessHeight); } public static RectF expand(RectF rect, float moreWidth, float moreHeight) { float halfMoreWidth = moreWidth / 2; float halfMoreHeight = moreHeight / 2; return new RectF(rect.left - halfMoreWidth, - rect.top - halfMoreHeight, - rect.right + halfMoreWidth, - rect.bottom + halfMoreHeight); + rect.top - halfMoreHeight, + rect.right + halfMoreWidth, + rect.bottom + halfMoreHeight); } public static RectF intersect(RectF one, RectF two) { @@ -95,34 +97,43 @@ public final class RectUtils { float x = rect.left * scale; float y = rect.top * scale; return new RectF(x, y, - x + (rect.width() * scale), - y + (rect.height() * scale)); + x + (rect.width() * scale), + y + (rect.height() * scale)); } + /** Returns the nearest integer rect of the given rect. */ 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)); + Math.round(rect.right), Math.round(rect.bottom)); } public static IntSize getSize(Rect rect) { return new IntSize(rect.width(), rect.height()); } + public static Point getOrigin(Rect rect) { + return new Point(rect.left, rect.top); + } + + public static PointF getOrigin(RectF rect) { + return new PointF(rect.left, rect.top); + } + /* * Returns the rect that represents a linear transition between `from` and `to` at time `t`, * which is on the scale [0, 1). */ public static RectF interpolate(RectF from, RectF to, float t) { return new RectF(FloatUtils.interpolate(from.left, to.left, t), - FloatUtils.interpolate(from.top, to.top, t), - FloatUtils.interpolate(from.right, to.right, t), - FloatUtils.interpolate(from.bottom, to.bottom, t)); + FloatUtils.interpolate(from.top, to.top, t), + FloatUtils.interpolate(from.right, to.right, t), + FloatUtils.interpolate(from.bottom, to.bottom, t)); } public static boolean fuzzyEquals(RectF a, RectF b) { return FloatUtils.fuzzyEquals(a.top, b.top) - && FloatUtils.fuzzyEquals(a.left, b.left) - && FloatUtils.fuzzyEquals(a.right, b.right) - && FloatUtils.fuzzyEquals(a.bottom, b.bottom); + && FloatUtils.fuzzyEquals(a.left, b.left) + && FloatUtils.fuzzyEquals(a.right, b.right) + && FloatUtils.fuzzyEquals(a.bottom, b.bottom); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java index e01e1cf369c0..68b7265b368b 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java @@ -20,6 +20,7 @@ * * Contributor(s): * Kartikaya Gupta + * Arkady Blyakher * * 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 @@ -41,19 +42,16 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; -import android.opengl.GLES11; -import android.opengl.GLES11Ext; -import android.util.Log; -import java.nio.ByteBuffer; -import javax.microedition.khronos.opengles.GL10; +import android.opengl.GLES20; import org.libreoffice.LOKitShell; import org.mozilla.gecko.util.FloatUtils; -//import org.mozilla.gecko.GeckoAppShell; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; /** * Draws a small rect. This is scaled to become a scrollbar. @@ -63,15 +61,52 @@ public class ScrollbarLayer extends TileLayer { private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame private static final int PADDING = 1; // gap between scrollbar and edge of viewport - private static final int BAR_SIZE = 12; + private static final int BAR_SIZE = 6; private static final int CAP_RADIUS = (BAR_SIZE / 2); - - private static final int[] CROPRECT_MIDPIXEL = new int[] { CAP_RADIUS, CAP_RADIUS, 1, 1 }; - private static final int[] CROPRECT_TOPCAP = new int[] { 0, CAP_RADIUS, BAR_SIZE, -CAP_RADIUS }; - private static final int[] CROPRECT_BOTTOMCAP = new int[] { 0, BAR_SIZE, BAR_SIZE, -CAP_RADIUS }; - private static final int[] CROPRECT_LEFTCAP = new int[] { 0, BAR_SIZE, CAP_RADIUS, -BAR_SIZE }; - private static final int[] CROPRECT_RIGHTCAP = new int[] { CAP_RADIUS, BAR_SIZE, CAP_RADIUS, -BAR_SIZE }; - + // Dimensions of the texture image + private static final float TEX_HEIGHT = 8.0f; + private static final float TEX_WIDTH = 8.0f; + // Texture coordinates for the scrollbar's body + // We take a 1x1 pixel from the center of the image and scale it to become the bar + private static final float[] BODY_TEX_COORDS = { + // x, y + CAP_RADIUS / TEX_WIDTH, CAP_RADIUS / TEX_HEIGHT, + CAP_RADIUS / TEX_WIDTH, (CAP_RADIUS + 1) / TEX_HEIGHT, + (CAP_RADIUS + 1) / TEX_WIDTH, CAP_RADIUS / TEX_HEIGHT, + (CAP_RADIUS + 1) / TEX_WIDTH, (CAP_RADIUS + 1) / TEX_HEIGHT + }; + // Texture coordinates for the top cap of the scrollbar + private static final float[] TOP_CAP_TEX_COORDS = { + // x, y + 0, 1.0f - CAP_RADIUS / TEX_HEIGHT, + 0, 1.0f, + BAR_SIZE / TEX_WIDTH, 1.0f - CAP_RADIUS / TEX_HEIGHT, + BAR_SIZE / TEX_WIDTH, 1.0f + }; + // Texture coordinates for the bottom cap of the scrollbar + private static final float[] BOT_CAP_TEX_COORDS = { + // x, y + 0, 1.0f - BAR_SIZE / TEX_HEIGHT, + 0, 1.0f - CAP_RADIUS / TEX_HEIGHT, + BAR_SIZE / TEX_WIDTH, 1.0f - BAR_SIZE / TEX_HEIGHT, + BAR_SIZE / TEX_WIDTH, 1.0f - CAP_RADIUS / TEX_HEIGHT + }; + // Texture coordinates for the left cap of the scrollbar + private static final float[] LEFT_CAP_TEX_COORDS = { + // x, y + 0, 1.0f - BAR_SIZE / TEX_HEIGHT, + 0, 1.0f, + CAP_RADIUS / TEX_WIDTH, 1.0f - BAR_SIZE / TEX_HEIGHT, + CAP_RADIUS / TEX_WIDTH, 1.0f + }; + // Texture coordinates for the right cap of the scrollbar + private static final float[] RIGHT_CAP_TEX_COORDS = { + // x, y + CAP_RADIUS / TEX_WIDTH, 1.0f - BAR_SIZE / TEX_HEIGHT, + CAP_RADIUS / TEX_WIDTH, 1.0f, + BAR_SIZE / TEX_WIDTH, 1.0f - BAR_SIZE / TEX_HEIGHT, + BAR_SIZE / TEX_WIDTH, 1.0f + }; private final boolean mVertical; private final ByteBuffer mBuffer; private final Bitmap mBitmap; @@ -89,26 +124,26 @@ public class ScrollbarLayer extends TileLayer { mCanvas = new Canvas(mBitmap); } + public static ScrollbarLayer create(boolean vertical) { + // just create an empty image for now, it will get drawn + // on demand anyway + int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE); + ByteBuffer buffer = LOKitShell.allocateDirectBuffer(imageSize * imageSize * 4); + CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, + CairoImage.FORMAT_ARGB32); + return new ScrollbarLayer(image, vertical, buffer); + } + protected void finalize() throws Throwable { try { if (!mFinalized && mBuffer != null) - /*GeckoAppShell*/ LOKitShell.freeDirectBuffer(mBuffer); + LOKitShell.freeDirectBuffer(mBuffer); mFinalized = true; } finally { super.finalize(); } } - - public static ScrollbarLayer create(boolean vertical) { - // just create an empty image for now, it will get drawn - // on demand anyway - int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE); - ByteBuffer buffer = /*GeckoAppShell*/LOKitShell.allocateDirectBuffer(imageSize * imageSize * 4); - CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32); - return new ScrollbarLayer(image, vertical, buffer); - } - /** * Decrease the opacity of the scrollbar by one frame's worth. * Return true if the opacity was decreased, or false if the scrollbars @@ -168,36 +203,197 @@ public class ScrollbarLayer extends TileLayer { return; try { - GLES11.glEnable(GL10.GL_BLEND); - GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); - Rect rect = RectUtils.round(mVertical ? getVerticalRect(context) : getHorizontalRect(context)); - GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID()); + Rect rect = RectUtils.round(mVertical + ? getVerticalRect(context) + : getHorizontalRect(context)); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); + float viewWidth = context.viewport.width(); float viewHeight = context.viewport.height(); - // for the body of the scrollbar, we take a 1x1 pixel from the center of the image - // and scale it to become the bar - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_MIDPIXEL, 0); - GLES11Ext.glDrawTexfOES(rect.left, viewHeight - rect.bottom, 0.0f, rect.width(), rect.height()); + float top = viewHeight - rect.top; + float bot = viewHeight - rect.bottom; + + // Coordinates for the scrollbar's body combined with the texture coordinates + float[] bodyCoords = { + // x, y, z, texture_x, texture_y + rect.left / viewWidth, bot / viewHeight, 0, + BODY_TEX_COORDS[0], BODY_TEX_COORDS[1], + + rect.left / viewWidth, (bot + rect.height()) / viewHeight, 0, + BODY_TEX_COORDS[2], BODY_TEX_COORDS[3], + + (rect.left + rect.width()) / viewWidth, bot / viewHeight, 0, + BODY_TEX_COORDS[4], BODY_TEX_COORDS[5], + + (rect.left + rect.width()) / viewWidth, (bot + rect.height()) / viewHeight, 0, + BODY_TEX_COORDS[6], BODY_TEX_COORDS[7] + }; + + // Get the buffer and handles from the context + FloatBuffer coordBuffer = context.coordBuffer; + int positionHandle = context.positionHandle; + int textureHandle = context.textureHandle; + + // Make sure we are at position zero in the buffer in case other draw methods did not + // clean up after themselves + coordBuffer.position(0); + coordBuffer.put(bodyCoords); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Reset the position in the buffer for the next set of vertex and texture coordinates. + coordBuffer.position(0); if (mVertical) { // top endcap - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_TOPCAP, 0); - GLES11Ext.glDrawTexfOES(rect.left, viewHeight - rect.top, 0.0f, BAR_SIZE, CAP_RADIUS); + float[] topCap = { + // x, y, z, texture_x, texture_y + rect.left / viewWidth, top / viewHeight, 0, + TOP_CAP_TEX_COORDS[0], TOP_CAP_TEX_COORDS[1], + + rect.left / viewWidth, (top + CAP_RADIUS) / viewHeight, 0, + TOP_CAP_TEX_COORDS[2], TOP_CAP_TEX_COORDS[3], + + (rect.left + BAR_SIZE) / viewWidth, top / viewHeight, 0, + TOP_CAP_TEX_COORDS[4], TOP_CAP_TEX_COORDS[5], + + (rect.left + BAR_SIZE) / viewWidth, (top + CAP_RADIUS) / viewHeight, 0, + TOP_CAP_TEX_COORDS[6], TOP_CAP_TEX_COORDS[7] + }; + + coordBuffer.put(topCap); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the + // buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Reset the position in the buffer for the next set of vertex and texture + // coordinates. + coordBuffer.position(0); + // bottom endcap - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_BOTTOMCAP, 0); - GLES11Ext.glDrawTexfOES(rect.left, viewHeight - (rect.bottom + CAP_RADIUS), 0.0f, BAR_SIZE, CAP_RADIUS); + float[] botCap = { + // x, y, z, texture_x, texture_y + rect.left / viewWidth, (bot - CAP_RADIUS) / viewHeight, 0, + BOT_CAP_TEX_COORDS[0], BOT_CAP_TEX_COORDS[1], + + rect.left / viewWidth, (bot) / viewHeight, 0, + BOT_CAP_TEX_COORDS[2], BOT_CAP_TEX_COORDS[3], + + (rect.left + BAR_SIZE) / viewWidth, (bot - CAP_RADIUS) / viewHeight, 0, + BOT_CAP_TEX_COORDS[4], BOT_CAP_TEX_COORDS[5], + + (rect.left + BAR_SIZE) / viewWidth, (bot) / viewHeight, 0, + BOT_CAP_TEX_COORDS[6], BOT_CAP_TEX_COORDS[7] + }; + + coordBuffer.put(botCap); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the + // buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Reset the position in the buffer for the next set of vertex and texture + // coordinates. + coordBuffer.position(0); } else { // left endcap - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_LEFTCAP, 0); - GLES11Ext.glDrawTexfOES(rect.left - CAP_RADIUS, viewHeight - rect.bottom, 0.0f, CAP_RADIUS, BAR_SIZE); + float[] leftCap = { + // x, y, z, texture_x, texture_y + (rect.left - CAP_RADIUS) / viewWidth, bot / viewHeight, 0, + LEFT_CAP_TEX_COORDS[0], LEFT_CAP_TEX_COORDS[1], + (rect.left - CAP_RADIUS) / viewWidth, (bot + BAR_SIZE) / viewHeight, 0, + LEFT_CAP_TEX_COORDS[2], LEFT_CAP_TEX_COORDS[3], + (rect.left) / viewWidth, bot / viewHeight, 0, LEFT_CAP_TEX_COORDS[4], + LEFT_CAP_TEX_COORDS[5], + (rect.left) / viewWidth, (bot + BAR_SIZE) / viewHeight, 0, + LEFT_CAP_TEX_COORDS[6], LEFT_CAP_TEX_COORDS[7] + }; + + coordBuffer.put(leftCap); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the + // buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + // Reset the position in the buffer for the next set of vertex and texture + // coordinates. + coordBuffer.position(0); + // right endcap - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_RIGHTCAP, 0); - GLES11Ext.glDrawTexfOES(rect.right, viewHeight - rect.bottom, 0.0f, CAP_RADIUS, BAR_SIZE); + float[] rightCap = { + // x, y, z, texture_x, texture_y + rect.right / viewWidth, (bot) / viewHeight, 0, + RIGHT_CAP_TEX_COORDS[0], RIGHT_CAP_TEX_COORDS[1], + + rect.right / viewWidth, (bot + BAR_SIZE) / viewHeight, 0, + RIGHT_CAP_TEX_COORDS[2], RIGHT_CAP_TEX_COORDS[3], + + (rect.right + CAP_RADIUS) / viewWidth, (bot) / viewHeight, 0, + RIGHT_CAP_TEX_COORDS[4], RIGHT_CAP_TEX_COORDS[5], + + (rect.right + CAP_RADIUS) / viewWidth, (bot + BAR_SIZE) / viewHeight, 0, + RIGHT_CAP_TEX_COORDS[6], RIGHT_CAP_TEX_COORDS[7] + }; + + coordBuffer.put(rightCap); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the + // buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, + coordBuffer); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } } finally { - GLES11.glDisable(GL10.GL_BLEND); + GLES20.glDisable(GLES20.GL_BLEND); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java index 8993b94d5ee1..d18579e1a117 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Arkady Blyakher * * 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 @@ -38,16 +39,14 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.gfx.CairoImage; +import org.mozilla.gecko.gfx.CairoUtils; import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.TileLayer; import android.graphics.PointF; import android.graphics.RectF; -import android.opengl.GLES11; -import android.opengl.GLES11Ext; +import android.opengl.GLES20; import android.util.Log; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; @@ -70,7 +69,7 @@ public class SingleTileLayer extends TileLayer { if (!initialized()) return; - GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID()); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); RectF bounds; int[] cropRect; @@ -80,21 +79,49 @@ public class SingleTileLayer extends TileLayer { if (repeats()) { bounds = new RectF(0.0f, 0.0f, viewport.width(), viewport.height()); int width = Math.round(viewport.width()); - int height = Math.round(-viewport.height()); - cropRect = new int[] { 0, size.height, width, height }; + int height = Math.round(viewport.height()); + cropRect = new int[] { 0, 0, width, height }; } else { bounds = getBounds(context, new FloatSize(size)); - cropRect = new int[] { 0, size.height, size.width, -size.height }; + cropRect = new int[] { 0, 0, size.width, size.height }; } - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, - 0); - float height = bounds.height(); float left = bounds.left - viewport.left; float top = viewport.height() - (bounds.top + height - viewport.top); - GLES11Ext.glDrawTexfOES(left, top, 0.0f, bounds.width(), height); + float[] coords = { + //x, y, z, texture_x, texture_y + left/viewport.width(), top/viewport.height(), 0, + cropRect[0]/(float)size.width, cropRect[1]/(float)size.height, + + left/viewport.width(), (top+height)/viewport.height(), 0, + cropRect[0]/(float)size.width, cropRect[3]/(float)size.height, + + (left+bounds.width())/viewport.width(), top/viewport.height(), 0, + cropRect[2]/(float)size.width, cropRect[1]/(float)size.height, + + (left+bounds.width())/viewport.width(), (top+height)/viewport.height(), 0, + cropRect[2]/(float)size.width, cropRect[3]/(float)size.height + }; + + FloatBuffer coordBuffer = context.coordBuffer; + int positionHandle = context.positionHandle; + int textureHandle = context.textureHandle; + + // Make sure we are at position zero in the buffer in case other draw methods did not clean + // up after themselves + coordBuffer.position(0); + coordBuffer.put(coords); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java index 047406234b44..e18139cb9bc9 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Arkady Blyakher * * 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 @@ -37,7 +38,7 @@ package org.mozilla.gecko.gfx; -import javax.microedition.khronos.opengles.GL10; +import android.opengl.GLES20; import java.util.ArrayList; /** Manages a list of dead tiles, so we don't leak resources. */ @@ -62,13 +63,13 @@ public class TextureReaper { mDeadTextureIDs.add(textureID); } - public void reap(GL10 gl) { + public void reap() { int[] deadTextureIDs = new int[mDeadTextureIDs.size()]; for (int i = 0; i < deadTextureIDs.length; i++) deadTextureIDs[i] = mDeadTextureIDs.get(i); mDeadTextureIDs.clear(); - gl.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0); + GLES20.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java index ad52229cfbcf..64ec94dc694a 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java @@ -20,6 +20,7 @@ * * Contributor(s): * Patrick Walton + * Arkady Blyakher * * 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 @@ -37,12 +38,12 @@ package org.mozilla.gecko.gfx; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Region; import android.opengl.GLES20; import android.util.Log; -import javax.microedition.khronos.opengles.GL10; -import javax.microedition.khronos.opengles.GL11Ext; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -104,7 +105,7 @@ public abstract class TileLayer extends Layer { return mImage.getSize().isPositive() && (mTextureIDs == null || !mDirtyRect.isEmpty()); } - private void validateTexture(GL10 gl) { + private void validateTexture() { /* Calculate the ideal texture size. This must be a power of two if * the texture is repeated or OpenGL ES 2.0 isn't supported, as * OpenGL ES 2.0 is required for NPOT texture support (without @@ -127,7 +128,7 @@ public abstract class TileLayer extends Layer { // Free the texture immediately, so we don't incur a // temporarily increased memory usage. - TextureReaper.get().reap(gl); + TextureReaper.get().reap(); } } } @@ -142,36 +143,38 @@ public abstract class TileLayer extends Layer { } @Override - protected boolean performUpdates(GL10 gl, RenderContext context) { - super.performUpdates(gl, context); + protected boolean performUpdates(RenderContext context) { + super.performUpdates(context); - if (mSkipTextureUpdate) + if (mSkipTextureUpdate) { return false; + } // Reallocate the texture if the size has changed - validateTexture(gl); + validateTexture(); // Don't do any work if the image has an invalid size. if (!mImage.getSize().isPositive()) return true; // If we haven't allocated a texture, assume the whole region is dirty - if (mTextureIDs == null) - uploadFullTexture(gl); - else - uploadDirtyRect(gl, mDirtyRect); + if (mTextureIDs == null) { + uploadFullTexture(); + } else { + uploadDirtyRect(mDirtyRect); + } mDirtyRect.setEmpty(); return true; } - private void uploadFullTexture(GL10 gl) { + private void uploadFullTexture() { IntSize bufferSize = mImage.getSize(); - uploadDirtyRect(gl, new Rect(0, 0, bufferSize.width, bufferSize.height)); + uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height)); } - private void uploadDirtyRect(GL10 gl, Rect dirtyRect) { + private void uploadDirtyRect(Rect dirtyRect) { // If we have nothing to upload, just return for now if (dirtyRect.isEmpty()) return; @@ -185,7 +188,7 @@ public abstract class TileLayer extends Layer { if (mTextureIDs == null) { mTextureIDs = new int[1]; - gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0); + GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); newlyCreated = true; } @@ -195,26 +198,27 @@ public abstract class TileLayer extends Layer { int cairoFormat = mImage.getFormat(); CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); - bindAndSetGLParameters(gl); + bindAndSetGLParameters(); if (newlyCreated || dirtyRect.contains(bufferRect)) { if (mSize.equals(bufferSize)) { - gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height, - 0, glInfo.format, glInfo.type, imageBuffer); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, + mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); return; } else { - gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height, - 0, glInfo.format, glInfo.type, null); - gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, bufferSize.width, bufferSize.height, - glInfo.format, glInfo.type, imageBuffer); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, + mSize.height, 0, glInfo.format, glInfo.type, null); + GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, bufferSize.width, + bufferSize.height, glInfo.format, glInfo.type, imageBuffer); return; } } // Make sure that the dirty region intersects with the buffer rect, // otherwise we'll end up with an invalid buffer pointer. - if (!Rect.intersects(dirtyRect, bufferRect)) + if (!Rect.intersects(dirtyRect, bufferRect)) { return; + } /* * Upload the changed rect. We have to widen to the full width of the texture @@ -232,19 +236,21 @@ public abstract class TileLayer extends Layer { } viewBuffer.position(position); - gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, - Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()), - glInfo.format, glInfo.type, viewBuffer); + GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, + Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()), + glInfo.format, glInfo.type, viewBuffer); } - private void bindAndSetGLParameters(GL10 gl) { - gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); + private void bindAndSetGLParameters() { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, + GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, + GLES20.GL_LINEAR); - int repeatMode = mRepeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE; - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode); - gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode); + int repeatMode = mRepeat ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE; + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java new file mode 100644 index 000000000000..9f443ea52894 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java @@ -0,0 +1,51 @@ +/* -*- 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 + * + * 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; + +public class ViewTransform { + public float x; + public float y; + public float scale; + + public ViewTransform(float inX, float inY, float inScale) { + x = inX; + y = inY; + scale = inScale; + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewportMetrics.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewportMetrics.java index d03b423df9bb..d98b47421520 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewportMetrics.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/ViewportMetrics.java @@ -38,22 +38,14 @@ package org.mozilla.gecko.gfx; -import android.graphics.Point; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.util.DisplayMetrics; -import org.libreoffice.LibreOfficeMainActivity; -import org.mozilla.gecko.util.FloatUtils; -//import org.mozilla.gecko.GeckoApp; -import org.mozilla.gecko.gfx.FloatSize; -import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.LayerController; -import org.mozilla.gecko.gfx.RectUtils; import org.json.JSONException; import org.json.JSONObject; -import android.util.Log; +import org.libreoffice.LibreOfficeMainActivity; +import org.mozilla.gecko.util.FloatUtils; /** * ViewportMetrics manages state and contains some utility functions related to @@ -61,28 +53,24 @@ import android.util.Log; */ public class ViewportMetrics { private static final String LOGTAG = "GeckoViewportMetrics"; - + private static final float MAX_BIAS = 0.8f; private FloatSize mPageSize; private RectF mViewportRect; private PointF mViewportOffset; private float mZoomFactor; - private boolean mAllowZoom; - // A scale from -1,-1 to 1,1 that represents what edge of the displayport // we want the viewport to be biased towards. private PointF mViewportBias; - private static final float MAX_BIAS = 0.8f; public ViewportMetrics() { DisplayMetrics metrics = new DisplayMetrics(); - /*GeckoApp*/LibreOfficeMainActivity.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); + LibreOfficeMainActivity.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); mPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels); mViewportRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels); mViewportOffset = new PointF(0, 0); mZoomFactor = 1.0f; mViewportBias = new PointF(0.0f, 0.0f); - mAllowZoom = true; } public ViewportMetrics(ViewportMetrics viewport) { @@ -92,21 +80,18 @@ public class ViewportMetrics { mViewportOffset = new PointF(offset.x, offset.y); mZoomFactor = viewport.getZoomFactor(); mViewportBias = viewport.mViewportBias; - mAllowZoom = viewport.mAllowZoom; } public ViewportMetrics(JSONObject json) throws JSONException { - 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"); - - mAllowZoom = json.getBoolean("allowZoom"); + 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 FloatSize(pageWidth, pageHeight); mViewportRect = new RectF(x, y, x + width, y + height); @@ -148,6 +133,29 @@ public class ViewportMetrics { return new PointF(mViewportRect.left, mViewportRect.top); } + public void setOrigin(PointF origin) { + // When the origin is set, we compare it with the last value set and + // change the viewport bias accordingly, so that any viewport based + // on these metrics will have a larger buffer in the direction of + // movement. + + // XXX Note the comment about bug #524925 in getOptimumViewportOffset. + // Ideally, the viewport bias would be a sliding scale, but we + // don't want to change it too often at the moment. + if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left)) + mViewportBias.x = 0; + else + mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS; + if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top)) + mViewportBias.y = 0; + else + mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS; + + mViewportRect.set(origin.x, origin.y, + origin.x + mViewportRect.width(), + origin.y + mViewportRect.height()); + } + public PointF getDisplayportOrigin() { return new PointF(mViewportRect.left - mViewportOffset.x, mViewportRect.top - mViewportOffset.y); @@ -157,11 +165,22 @@ public class ViewportMetrics { return new FloatSize(mViewportRect.width(), mViewportRect.height()); } + public void setSize(FloatSize size) { + mViewportRect.right = mViewportRect.left + size.width; + mViewportRect.bottom = mViewportRect.top + size.height; + } + public RectF getViewport() { return mViewportRect; } - /** Returns the viewport rectangle, clamped within the page-size. */ + public void setViewport(RectF viewport) { + mViewportRect = viewport; + } + + /** + * Returns the viewport rectangle, clamped within the page-size. + */ public RectF getClampedViewport() { RectF clampedViewport = new RectF(mViewportRect); @@ -185,56 +204,20 @@ public class ViewportMetrics { return mViewportOffset; } - public FloatSize getPageSize() { - return mPageSize; - } - - public float getZoomFactor() { - return mZoomFactor; + public void setViewportOffset(PointF offset) { + mViewportOffset = offset; } - public boolean getAllowZoom() { - return mAllowZoom; + public FloatSize getPageSize() { + return mPageSize; } public void setPageSize(FloatSize pageSize) { mPageSize = pageSize; } - public void setViewport(RectF viewport) { - mViewportRect = viewport; - } - - public void setOrigin(PointF origin) { - // When the origin is set, we compare it with the last value set and - // change the viewport bias accordingly, so that any viewport based - // on these metrics will have a larger buffer in the direction of - // movement. - - // XXX Note the comment about bug #524925 in getOptimumViewportOffset. - // Ideally, the viewport bias would be a sliding scale, but we - // don't want to change it too often at the moment. - if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left)) - mViewportBias.x = 0; - else - mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS; - if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top)) - mViewportBias.y = 0; - else - mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS; - - mViewportRect.set(origin.x, origin.y, - origin.x + mViewportRect.width(), - origin.y + mViewportRect.height()); - } - - public void setSize(FloatSize size) { - mViewportRect.right = mViewportRect.left + size.width; - mViewportRect.bottom = mViewportRect.top + size.height; - } - - public void setViewportOffset(PointF offset) { - mViewportOffset = offset; + public float getZoomFactor() { + return mZoomFactor; } public void setZoomFactor(float zoomFactor) { @@ -320,3 +303,4 @@ public class ViewportMetrics { return buff.toString(); } } + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java new file mode 100644 index 000000000000..0f314ae71620 --- /dev/null +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java @@ -0,0 +1,80 @@ +/* -*- 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 + * Chris Lord + * + * 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 android.graphics.Point; + +public class VirtualLayer extends Layer { + private Listener mListener; + private IntSize mSize; + + public void setListener(Listener listener) { + mListener = listener; + } + + @Override + public void draw(RenderContext context) { + // No-op. + } + + @Override + public IntSize getSize() { + return mSize; + } + + public void setSize(IntSize size) { + mSize = size; + } + + @Override + protected boolean performUpdates(RenderContext context) { + boolean dimensionsChanged = dimensionChangesPending(); + boolean result = super.performUpdates(context); + if (dimensionsChanged && mListener != null) { + mListener.dimensionsChanged(getOrigin(), getResolution()); + } + + return result; + } + + public interface Listener { + void dimensionsChanged(Point newOrigin, float newResolution); + } +} + diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/WidgetTileLayer.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/WidgetTileLayer.java index 7cbcdb3a016d..b123d55c403d 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/WidgetTileLayer.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/gfx/WidgetTileLayer.java @@ -20,6 +20,7 @@ * * Contributor(s): * James Willcox + * Arkady Blyakher * * 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 @@ -41,11 +42,10 @@ import org.libreoffice.LOKitShell; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.SingleTileLayer; //import org.mozilla.gecko.GeckoAppShell; -import android.opengl.GLES11; -import android.opengl.GLES11Ext; import android.graphics.RectF; import android.util.Log; -import javax.microedition.khronos.opengles.GL10; +import android.opengl.GLES20; +import java.nio.FloatBuffer; /** * Encapsulates the logic needed to draw the single-tiled Gecko texture @@ -66,9 +66,9 @@ public class WidgetTileLayer extends Layer { public IntSize getSize() { return mImage.getSize(); } protected void bindAndSetGLParameters() { - GLES11.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]); - GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); - GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); } @Override @@ -78,16 +78,16 @@ public class WidgetTileLayer extends Layer { } @Override - protected boolean performUpdates(GL10 gl, RenderContext context) { - super.performUpdates(gl, context); + protected boolean performUpdates(RenderContext context) { + super.performUpdates(context); if (mTextureIDs == null) { mTextureIDs = new int[1]; - GLES11.glGenTextures(1, mTextureIDs, 0); + GLES20.glGenTextures(1, mTextureIDs, 0); } bindAndSetGLParameters(); - /*GeckoAppShell*/LOKitShell.bindWidgetTexture(); + LOKitShell.bindWidgetTexture(); return true; } @@ -99,7 +99,7 @@ public class WidgetTileLayer extends Layer { if (!initialized()) return; - GLES11.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); RectF bounds; int[] cropRect; @@ -107,21 +107,52 @@ public class WidgetTileLayer extends Layer { RectF viewport = context.viewport; bounds = getBounds(context, new FloatSize(size)); - cropRect = new int[] { 0, size.height, size.width, -size.height }; + cropRect = new int[] { 0, 0, size.width, size.height }; bounds.offset(-viewport.left, -viewport.top); - GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, - 0); - float top = viewport.height() - (bounds.top + bounds.height()); // There may be errors from a previous GL call, so clear them first because // we want to check for one below - while (GLES11.glGetError() != GLES11.GL_NO_ERROR); + while (GLES20.glGetError() != GLES20.GL_NO_ERROR); + + float[] coords = { + //x, y, z, texture_x, texture_y + bounds.left/viewport.width(), top/viewport.height(), 0, + cropRect[0]/size.width, cropRect[1]/size.height, + + bounds.left/viewport.width(), (top+bounds.height())/viewport.height(), 0, + cropRect[0]/size.width, cropRect[3]/size.height, + + (bounds.left+bounds.width())/viewport.width(), top/viewport.height(), 0, + cropRect[2]/size.width, cropRect[1]/size.height, + + (bounds.left+bounds.width())/viewport.width(), (top+bounds.height())/viewport.height(), + 0, + cropRect[2]/size.width, cropRect[3]/size.height + }; + + // Get the buffer and handles from the context + FloatBuffer coordBuffer = context.coordBuffer; + int positionHandle = context.positionHandle; + int textureHandle = context.textureHandle; + + // Make sure we are at position zero in the buffer in case other draw methods did not clean + // up after themselves + coordBuffer.position(0); + coordBuffer.put(coords); + + // Vertex coordinates are x,y,z starting at position 0 into the buffer. + coordBuffer.position(0); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); + + // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. + coordBuffer.position(3); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLES11Ext.glDrawTexfOES(bounds.left, top, 0.0f, bounds.width(), bounds.height()); - int error = GLES11.glGetError(); - if (error != GLES11.GL_NO_ERROR) { + int error = GLES20.glGetError(); + if (error != GLES20.GL_NO_ERROR) { Log.i(LOGTAG, "Failed to draw texture: " + error); } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/Axis.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/Axis.java index 1d8138de7d51..8c0fce4c73b3 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/Axis.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/Axis.java @@ -61,7 +61,7 @@ abstract class Axis { // The rate of deceleration when the surface has overscrolled. private static final float OVERSCROLL_DECEL_RATE = 0.04f; // The percentage of the surface which can be overscrolled before it must snap back. - private static final float SNAP_LIMIT = 0.3f; + private static final float SNAP_LIMIT = 0.75f; // The minimum amount of space that must be present for an axis to be considered scrollable, // in pixels. @@ -268,4 +268,4 @@ abstract class Axis { mDisplacement = 0.0f; return d; } -} \ No newline at end of file +} diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/PanZoomController.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/PanZoomController.java index 58c52e26ff99..c3cacccf805e 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/PanZoomController.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/PanZoomController.java @@ -47,9 +47,6 @@ import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.PointUtils; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.util.FloatUtils; -//import org.mozilla.gecko.GeckoApp; -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoEventListener; import android.graphics.PointF; import android.graphics.RectF; @@ -89,7 +86,7 @@ public class PanZoomController private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees // The maximum amount we allow you to zoom into a page - private static final float MAX_ZOOM = 12.0f; + private static final float MAX_ZOOM = 4.0f; /* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */ private static final float[] EASE_OUT_ANIMATION_FRAMES = { @@ -148,7 +145,7 @@ public class PanZoomController mX = new AxisX(mSubscroller); mY = new AxisY(mSubscroller); - mMainThread = /*GeckoApp*/LibreOfficeMainActivity.mAppContext.getMainLooper().getThread(); + mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread(); checkMainThread(); mState = PanZoomState.NOTHING; @@ -254,6 +251,7 @@ public class PanZoomController /* * Panning/scrolling */ + private boolean onTouchStart(MotionEvent event) { Log.d(LOGTAG, "onTouchStart in state " + mState); // user is taking control of movement, so stop @@ -298,12 +296,12 @@ public class PanZoomController cancelTouch(); startPanning(event.getX(0), event.getY(0), event.getEventTime()); //GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); track(event); return true; case PANNING_HOLD_LOCKED: - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); mState = PanZoomState.PANNING_LOCKED; // fall through case PANNING_LOCKED: @@ -311,7 +309,7 @@ public class PanZoomController return true; case PANNING_HOLD: - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); mState = PanZoomState.PANNING; // fall through case PANNING: @@ -761,7 +759,7 @@ public class PanZoomController mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); //GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */); - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); cancelTouch(); return true; @@ -771,12 +769,6 @@ public class PanZoomController public boolean onScale(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScale in state " + mState); - //if (GeckoApp.mDOMFullScreen) - // return false; - - if (!mController.getViewportMetrics().getAllowZoom()) - return false; - if (mState == PanZoomState.ANIMATED_ZOOM) return false; @@ -837,7 +829,7 @@ public class PanZoomController } public boolean getRedrawHint() { - return (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING); + return (mState != PanZoomState.PINCHING && mState != PanZoomState.ANIMATED_ZOOM); } private void sendPointToGecko(String event, MotionEvent motionEvent) { @@ -870,7 +862,7 @@ public class PanZoomController @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); sendPointToGecko("Gesture:SingleTap", motionEvent); return true; } @@ -887,7 +879,7 @@ public class PanZoomController } private boolean animatedZoomTo(RectF zoomToRect) { - //GeckoApp.mFormAssistPopup.hide(); + //GeckoApp.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; final float startZoom = mController.getZoomFactor(); @@ -926,4 +918,4 @@ public class PanZoomController bounce(finalMetrics); return true; } -} \ No newline at end of file +} diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java index 5184bd18a462..4f9e39857e81 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java @@ -81,19 +81,19 @@ public class SimpleScaleGestureDetector { /** Forward touch events to this function. */ public void onTouchEvent(MotionEvent event) { - switch (event.getAction() & event.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - onTouchStart(event); - break; - case MotionEvent.ACTION_MOVE: - onTouchMove(event); - break; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - onTouchEnd(event); - break; + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + onTouchStart(event); + break; + case MotionEvent.ACTION_MOVE: + onTouchMove(event); + break; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + onTouchEnd(event); + break; } } @@ -103,7 +103,7 @@ public class SimpleScaleGestureDetector { private int getActionIndex(MotionEvent event) { return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; } private void onTouchStart(MotionEvent event) { @@ -156,11 +156,11 @@ public class SimpleScaleGestureDetector { */ public float getFocusX() { switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().x; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f; + case 1: + return mPointerInfo.getFirst().getCurrent().x; + case 2: + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f; } Log.e(LOGTAG, "No gesture taking place in getFocusX()!"); @@ -173,11 +173,11 @@ public class SimpleScaleGestureDetector { */ public float getFocusY() { switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().y; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f; + case 1: + return mPointerInfo.getFirst().getCurrent().y; + case 2: + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f; } Log.e(LOGTAG, "No gesture taking place in getFocusY()!"); @@ -225,9 +225,9 @@ public class SimpleScaleGestureDetector { /* Sends the requested scale gesture notification to the listener. */ private void sendScaleGesture(EventType eventType) { switch (eventType) { - case BEGIN: mListener.onScaleBegin(this); break; - case CONTINUE: mListener.onScale(this); break; - case END: mListener.onScaleEnd(this); break; + case BEGIN: mListener.onScaleBegin(this); break; + case CONTINUE: mListener.onScale(this); break; + case END: mListener.onScaleEnd(this); break; } } diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SubdocumentScrollHelper.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SubdocumentScrollHelper.java index c7da61acda2b..f24a5b7adaa1 100644 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SubdocumentScrollHelper.java +++ b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SubdocumentScrollHelper.java @@ -37,15 +37,15 @@ package org.mozilla.gecko.ui; -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.GeckoEventListener; -import org.json.JSONObject; -import org.json.JSONException; + import android.graphics.PointF; import android.os.Handler; import android.util.Log; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.gecko.GeckoEventListener; + class SubdocumentScrollHelper implements GeckoEventListener { private static final String LOGTAG = "GeckoSubdocumentScrollHelper"; @@ -73,11 +73,11 @@ class SubdocumentScrollHelper implements GeckoEventListener { } boolean scrollBy(PointF displacement) { - if (! mOverridePanning) { + if (!mOverridePanning) { return false; } - if (! mOverrideScrollAck) { + if (!mOverrideScrollAck) { mOverrideScrollPending = true; return true; } @@ -92,7 +92,7 @@ class SubdocumentScrollHelper implements GeckoEventListener { } catch (JSONException e) { Log.e(LOGTAG, "Error forming subwindow scroll message: ", e); } - //GeckoAppShell.sendEventToGecko(new GeckoEvent(MESSAGE_SCROLL, json.toString())); + //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString())); return true; } -- cgit