Kaydet (Commit) 58f5e531 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl

android: import changes from Fennec to get ScreenShotLayer

Change-Id: I1b72af85a906aef289d1be086274941094df4f96
üst cd763063
......@@ -40,6 +40,7 @@ package org.mozilla.gecko.gfx;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
......@@ -53,11 +54,13 @@ import java.util.List;
public class GeckoLayerClient {
private static final String LOGTAG = "GeckoLayerClient";
private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
private static final int DEFAULT_DISPLAY_PORT_MARGIN = 300;
private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
private static final IntSize TILE_SIZE = new IntSize(256, 256);
protected IntSize mScreenSize;
private RectF mDisplayPort;
protected Layer mTileLayer;
/* The viewport that Gecko is currently displaying. */
protected ViewportMetrics mGeckoViewport;
......@@ -79,6 +82,7 @@ public class GeckoLayerClient {
public GeckoLayerClient(Context context) {
mContext = context;
mScreenSize = new IntSize(0, 0);
mDisplayPort = new RectF();
}
protected void setupLayer() {
......@@ -110,7 +114,7 @@ public class GeckoLayerClient {
layerController.setViewportMetrics(mGeckoViewport);
}
sendResizeEventIfNecessary(false);
sendResizeEventIfNecessary(true);
}
public void beginDrawing(ViewportMetrics viewportMetrics) {
......@@ -132,6 +136,10 @@ public class GeckoLayerClient {
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
}
RectF getDisplayPort() {
return mDisplayPort;
}
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
......@@ -142,7 +150,8 @@ public class GeckoLayerClient {
mGeckoViewport.setSize(viewportSize);
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
RectF position = mGeckoViewport.getViewport();
mTileLayer.setPosition(RectUtils.round(position));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport);
......@@ -160,7 +169,7 @@ public class GeckoLayerClient {
}
/* Informs Gecko that the screen size has changed. */
protected void sendResizeEventIfNecessary(boolean force) {
private void sendResizeEventIfNecessary(boolean force) {
Log.e(LOGTAG, "### sendResizeEventIfNecessary " + force);
DisplayMetrics metrics = new DisplayMetrics();
......@@ -172,9 +181,8 @@ public class GeckoLayerClient {
// size is zero (which indicates that the rendering surface hasn't been
// allocated yet).
boolean screenSizeChanged = !mScreenSize.equals(newScreenSize);
boolean viewportSizeValid = (mLayerController != null && mLayerController.getViewportSize().isPositive());
if (!(force || (screenSizeChanged && viewportSizeValid))) {
if (!force && !screenSizeChanged) {
return;
}
......@@ -213,11 +221,75 @@ public class GeckoLayerClient {
mViewportSizeChanged = true;
}
private static RectF calculateDisplayPort(ImmutableViewportMetrics metrics) {
float desiredXMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
float desiredYMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less.
// figure out how much of the desired buffer amount we can actually use on the horizontal axis
float xBufferAmount = Math.min(desiredXMargins, metrics.pageSizeWidth - metrics.getWidth());
// if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
// use it on the vertical axis
float savedPixels = (desiredXMargins - xBufferAmount) * (metrics.getHeight() + desiredYMargins);
float extraYAmount = (float)Math.floor(savedPixels / (metrics.getWidth() + xBufferAmount));
float yBufferAmount = Math.min(desiredYMargins + extraYAmount, metrics.pageSizeHeight - metrics.getHeight());
// and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
if (xBufferAmount == desiredXMargins && yBufferAmount < desiredYMargins) {
savedPixels = (desiredYMargins - yBufferAmount) * (metrics.getWidth() + xBufferAmount);
float extraXAmount = (float)Math.floor(savedPixels / (metrics.getHeight() + yBufferAmount));
xBufferAmount = Math.min(xBufferAmount + extraXAmount, metrics.pageSizeWidth - metrics.getWidth());
}
// and now calculate the display port margins based on how much buffer we've decided to use and
// the page bounds, ensuring we use all of the available buffer amounts on one side or the other
// on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
// entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
// is split).
float leftMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.viewportRectLeft);
float rightMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.pageSizeWidth - (metrics.viewportRectLeft + metrics.getWidth()));
if (leftMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
rightMargin = xBufferAmount - leftMargin;
} else if (rightMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
leftMargin = xBufferAmount - rightMargin;
} else if (!FloatUtils.fuzzyEquals(leftMargin + rightMargin, xBufferAmount)) {
float delta = xBufferAmount - leftMargin - rightMargin;
leftMargin += delta / 2;
rightMargin += delta / 2;
}
float topMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.viewportRectTop);
float bottomMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, metrics.pageSizeHeight - (metrics.viewportRectTop + metrics.getHeight()));
if (topMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
bottomMargin = yBufferAmount - topMargin;
} else if (bottomMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
topMargin = yBufferAmount - bottomMargin;
} else if (!FloatUtils.fuzzyEquals(topMargin + bottomMargin, yBufferAmount)) {
float delta = yBufferAmount - topMargin - bottomMargin;
topMargin += delta / 2;
bottomMargin += delta / 2;
}
// note that unless the viewport size changes, or the page dimensions change (either because of
// content changes or zooming), the size of the display port should remain constant. this
// is intentional to avoid re-creating textures and all sorts of other reallocations in the
// draw and composition code.
return new RectF(metrics.viewportRectLeft - leftMargin,
metrics.viewportRectTop - topMargin,
metrics.viewportRectRight + rightMargin,
metrics.viewportRectBottom + bottomMargin);
}
private void adjustViewport() {
ViewportMetrics viewportMetrics = new ViewportMetrics(mLayerController.getViewportMetrics());
ViewportMetrics viewportMetrics =
new ViewportMetrics(mLayerController.getViewportMetrics());
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
mDisplayPort = calculateDisplayPort(new ImmutableViewportMetrics(mLayerController.getViewportMetrics()));
LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics));
if (mViewportSizeChanged) {
mViewportSizeChanged = false;
......@@ -228,19 +300,18 @@ public class GeckoLayerClient {
}
public void geometryChanged() {
sendResizeEventIfNecessary();
render();
sendResizeEventIfNecessary(false);
if (mLayerController.getRedrawHint())
adjustViewport();
}
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);
}
public void addTile(SubTile tile) {
if (mTileLayer instanceof MultiTileLayer) {
......
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.graphics.PointF;
import android.graphics.RectF;
/**
* ImmutableViewportMetrics are used to store the viewport metrics
* in way that we can access a version of them from multiple threads
* without having to take a lock
*/
public class ImmutableViewportMetrics {
// We need to flatten the RectF and FloatSize structures
// because Java doesn't have the concept of const classes
public final float pageSizeWidth;
public final float pageSizeHeight;
public final float viewportRectBottom;
public final float viewportRectLeft;
public final float viewportRectRight;
public final float viewportRectTop;
public final float zoomFactor;
public ImmutableViewportMetrics(ViewportMetrics m) {
RectF viewportRect = m.getViewport();
viewportRectBottom = viewportRect.bottom;
viewportRectLeft = viewportRect.left;
viewportRectRight = viewportRect.right;
viewportRectTop = viewportRect.top;
FloatSize pageSize = m.getPageSize();
pageSizeWidth = pageSize.width;
pageSizeHeight = pageSize.height;
zoomFactor = m.getZoomFactor();
}
public float getWidth() {
return viewportRectRight - viewportRectLeft;
}
public float getHeight() {
return viewportRectBottom - viewportRectTop;
}
// some helpers to make ImmutableViewportMetrics act more like ViewportMetrics
public PointF getOrigin() {
return new PointF(viewportRectLeft, viewportRectTop);
}
public FloatSize getSize() {
return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
}
public RectF getViewport() {
return new RectF(viewportRectLeft,
viewportRectTop,
viewportRectRight,
viewportRectBottom);
}
public FloatSize getPageSize() {
return new FloatSize(pageSizeWidth, pageSizeHeight);
}
}
......@@ -39,7 +39,7 @@
package org.mozilla.gecko.gfx;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
......@@ -50,16 +50,24 @@ 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 Rect mNewPosition;
private float mNewResolution;
private LayerView mView;
protected Rect mPosition;
protected float mResolution;
public Layer() {
this(null);
}
public Layer(IntSize size) {
mTransactionLock = new ReentrantLock();
mOrigin = new Point(0, 0);
if (size == null) {
mPosition = new Rect();
} else {
mPosition = new Rect(0, 0, size.width, size.height);
}
mResolution = 1.0f;
}
......@@ -69,12 +77,14 @@ public abstract class Layer {
*/
public final boolean update(RenderContext context) {
if (mTransactionLock.isHeldByCurrentThread()) {
throw new RuntimeException("draw() called while transaction lock held by this thread?!");
throw new RuntimeException("draw() called while transaction lock held by this " +
"thread?!");
}
if (mTransactionLock.tryLock()) {
try {
return performUpdates(context);
performUpdates(context);
return true;
} finally {
mTransactionLock.unlock();
}
......@@ -83,24 +93,12 @@ 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.
*/
public abstract IntSize getSize();
/**
* 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;
float width = size.width * scaleFactor, height = size.height * scaleFactor;
return new RectF(x, y, x + width, y + height);
/** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */
protected RectF getBounds(RenderContext context) {
return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution);
}
/**
......@@ -109,68 +107,50 @@ public abstract class Layer {
* may be overridden.
*/
public Region getValidRegion(RenderContext context) {
return new Region(RectUtils.round(getBounds(context, new FloatSize(getSize()))));
return new Region(RectUtils.round(getBounds(context)));
}
/**
* 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.
* <p/>
*
* This function may block, so you should never call this on the main UI thread.
*/
public void beginTransaction(LayerView aView) {
//if (mTransactionLock.isHeldByCurrentThread())
// throw new RuntimeException("Nested transactions are not supported");
public void beginTransaction() {
if (mTransactionLock.isHeldByCurrentThread())
throw new RuntimeException("Nested transactions are not supported");
mTransactionLock.lock();
mView = aView;
mInTransaction = true;
mNewResolution = mResolution;
}
public void beginTransaction() {
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");
mInTransaction = false;
mTransactionLock.unlock();
if (mView != null)
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.
*/
public Point getOrigin() {
return mOrigin;
/** Returns the current layer position. */
public Rect getPosition() {
return mPosition;
}
/**
* Sets the origin. Only valid inside a transaction.
*/
public void setOrigin(Point newOrigin) {
/** Sets the position. Only valid inside a transaction. */
public void setPosition(Rect newPosition) {
if (!mInTransaction)
throw new RuntimeException("setOrigin() is only valid inside a transaction");
mNewOrigin = newOrigin;
throw new RuntimeException("setPosition() is only valid inside a transaction");
mNewPosition = newPosition;
}
/**
* Returns the current layer's resolution.
*/
/** Returns the current layer's resolution. */
public float getResolution() {
return mResolution;
}
......@@ -179,8 +159,7 @@ 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");
......@@ -193,22 +172,15 @@ public abstract class Layer {
* superclass implementation. Returns false if there is still work to be done after this
* update is complete.
*/
protected boolean performUpdates(RenderContext context) {
if (mNewOrigin != null) {
mOrigin = mNewOrigin;
mNewOrigin = null;
protected void performUpdates(RenderContext context) {
if (mNewPosition != null) {
mPosition = mNewPosition;
mNewPosition = null;
}
if (mNewResolution != 0.0f) {
mResolution = mNewResolution;
mNewResolution = 0.0f;
}
return true;
}
protected boolean dimensionChangesPending() {
return (mNewOrigin != null) || (mNewResolution != 0.0f);
}
public static class RenderContext {
......@@ -234,8 +206,8 @@ public abstract class Layer {
return false;
}
return RectUtils.fuzzyEquals(viewport, other.viewport)
&& pageSize.fuzzyEquals(other.pageSize)
&& FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
&& pageSize.fuzzyEquals(other.pageSize)
&& FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
}
}
}
......
......@@ -39,21 +39,18 @@
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.graphics.Bitmap;
import android.util.Log;
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 org.mozilla.gecko.ui.SimpleScaleGestureDetector;
import java.nio.IntBuffer;
import java.util.LinkedList;
......@@ -62,6 +59,8 @@ 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.
*
* Note that LayerView is accessed by Robocop via reflection.
*/
public class LayerView extends FlexibleGLSurfaceView {
private Context mContext;
......@@ -75,6 +74,14 @@ public class LayerView extends FlexibleGLSurfaceView {
private static String LOGTAG = "GeckoLayerView";
/* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */
private LinkedList<MotionEvent> mEventQueue = new LinkedList<MotionEvent>();
/* Must be a PAINT_xxx constant */
private int mPaintState = PAINT_NONE;
/* Flags used to determine when to show the painted surface. The integer
* order must correspond to the order in which these states occur. */
public static final int PAINT_NONE = 0;
public static final int PAINT_BEFORE_FIRST = 1;
public static final int PAINT_AFTER_FIRST = 2;
public LayerView(Context context, LayerController controller) {
......@@ -86,7 +93,7 @@ public class LayerView extends FlexibleGLSurfaceView {
setRenderer(mRenderer);
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
mScaleGestureDetector =
new SimpleScaleGestureDetector(controller.getScaleGestureListener());
new SimpleScaleGestureDetector(controller.getScaleGestureListener());
mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener());
mInputConnectionHandler = null;
......@@ -139,12 +146,6 @@ public class LayerView extends FlexibleGLSurfaceView {
mController.setViewportSize(new FloatSize(size));
}
//public GeckoInputConnection setInputConnectionHandler() {
// GeckoInputConnection geckoInputConnection = GeckoInputConnection.create(this);
// mInputConnectionHandler = geckoInputConnection;
// return geckoInputConnection;
//}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (mInputConnectionHandler != null)
......@@ -187,6 +188,7 @@ public class LayerView extends FlexibleGLSurfaceView {
return false;
}
@Override
public void requestRender() {
super.requestRender();
......@@ -199,6 +201,10 @@ public class LayerView extends FlexibleGLSurfaceView {
}
}
public void changeCheckerboardBitmap(Bitmap bitmap) {
mRenderer.setCheckerboardBitmap(bitmap);
}
public void addLayer(Layer layer) {
mRenderer.addLayer(layer);
}
......@@ -226,5 +232,27 @@ public class LayerView extends FlexibleGLSurfaceView {
public IntBuffer getPixels() {
return mRenderer.getPixels();
}
public void setLayerRenderer(LayerRenderer renderer) {
mRenderer = renderer;
setRenderer(mRenderer);
}
public LayerRenderer getLayerRenderer() {
return mRenderer;
}
/* paintState must be a PAINT_xxx constant. The state will only be changed
* if paintState represents a state that occurs after the current state. */
public void setPaintState(int paintState) {
if (paintState > mPaintState) {
Log.d(LOGTAG, "LayerView paint state set to " + paintState);
mPaintState = paintState;
}
}
public int getPaintState() {
return mPaintState;
}
}
......@@ -70,12 +70,12 @@ public class MultiTileLayer extends Layer {
}
for (SubTile layer : mTiles) {
IntSize tileSize = layer.getSize();
Rect tileRect = new Rect(layer.x, layer.y, layer.x + tileSize.width, layer.y + tileSize.height);
Rect rect = layer.getPosition();
Rect tileRect = new Rect(layer.x, layer.y, layer.x + rect.width(), layer.y + rect.height());
if (tileRect.intersect(dirtyRect)) {
tileRect.offset(-layer.x, -layer.y);
layer.invalidate(tileRect);
layer.invalidate();
}
}
}
......@@ -90,7 +90,6 @@ public class MultiTileLayer extends Layer {
mSize = size;
}
@Override
public IntSize getSize() {
return mSize;
}
......@@ -99,11 +98,12 @@ public class MultiTileLayer extends Layer {
Log.i(LOGTAG, "validateTiles()");
// Set tile origins and resolution
refreshTileMetrics(getOrigin(), getResolution(), false);
Point origin = new Point();
refreshTileMetrics(origin, getResolution(), false);
}
@Override
protected boolean performUpdates(RenderContext context) {
protected void performUpdates(RenderContext context) {
super.performUpdates(context);
validateTiles();
......@@ -115,34 +115,20 @@ public class MultiTileLayer extends Layer {
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(context);
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
boolean isDirty = layer.isDirty();
if (isDirty) {
if (!RectF.intersects(layerBounds, context.viewport)) {
if (firstDirtyTile == null)
firstDirtyTile = layer;
dirtyTiles++;
invalid = true;
} else {
// This tile intersects with the screen and is dirty,
// update it immediately.
layer.setSkipTextureUpdate(false);
screenUpdateDone = true;
layer.performUpdates(context);
invalid = false;
}
RectF layerBounds = layer.getBounds(context);
if (!RectF.intersects(layerBounds, context.viewport)) {
if (firstDirtyTile == null)
firstDirtyTile = layer;
dirtyTiles++;
} else {
// This tile intersects with the screen and is dirty,
// update it immediately.
screenUpdateDone = true;
layer.performUpdates(context);
}
// We use the SkipTextureUpdate flag as a marker of a tile's
// validity. This is required, as sometimes layers are drawn
// without updating first, and we mustn't draw tiles that have
// been marked as invalid that we haven't updated.
layer.setSkipTextureUpdate(invalid);
}
// Now if no tiles that intersect with the screen were updated, update
......@@ -150,12 +136,10 @@ public class MultiTileLayer extends Layer {
// of spreading out non-critical texture upload over time, and smoothing
// upload-related hitches.
if (!screenUpdateDone && firstDirtyTile != null) {
firstDirtyTile.setSkipTextureUpdate(false);
firstDirtyTile.performUpdates(context);
dirtyTiles--;
}
return (dirtyTiles == 0);
}
private void refreshTileMetrics(Point origin, float resolution, boolean inTransaction) {
......@@ -166,7 +150,7 @@ public class MultiTileLayer extends Layer {
}
if (origin != null) {
layer.setOrigin(new Point(origin.x + layer.x, origin.y + layer.y));
layer.getPosition().offsetTo(origin.x + layer.x, origin.y + layer.y);
}
if (resolution >= 0.0f) {
layer.setResolution(resolution);
......@@ -178,9 +162,8 @@ public class MultiTileLayer extends Layer {
}
}
@Override
public void setOrigin(Point newOrigin) {
super.setOrigin(newOrigin);
public void setPosition(Point newOrigin) {
super.getPosition().offsetTo(newOrigin.x, newOrigin.y);
refreshTileMetrics(newOrigin, -1, true);
}
......@@ -211,13 +194,8 @@ public class MultiTileLayer extends Layer {
@Override
public void draw(RenderContext context) {
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())
continue;
// Avoid work, only draw tiles that intersect with the viewport
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
RectF layerBounds = layer.getBounds(context);
if (RectF.intersects(layerBounds, context.viewport))
layer.draw(context);
}
......@@ -227,8 +205,6 @@ public class MultiTileLayer extends Layer {
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);
}
......
......@@ -20,6 +20,7 @@
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Arkady Blyakher <rkadyb@mit.edu>
*
* 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,13 +38,9 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import android.graphics.PointF;
import android.graphics.RectF;
import android.opengl.GLES11;
import android.opengl.GLES11Ext;
import android.util.Log;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import java.nio.FloatBuffer;
/**
......@@ -54,10 +51,10 @@ import java.nio.FloatBuffer;
*/
public class NinePatchTileLayer extends TileLayer {
private static final int PATCH_SIZE = 16;
private static final int TEXTURE_SIZE = 48;
private static final int TEXTURE_SIZE = 64;
public NinePatchTileLayer(CairoImage image) {
super(false, image);
super(image, PaintMode.NORMAL);
}
@Override
......@@ -65,14 +62,11 @@ public class NinePatchTileLayer extends TileLayer {
if (!initialized())
return;
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
GLES11.glEnable(GL10.GL_BLEND);
try {
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
drawPatches(context);
} finally {
GLES11.glDisable(GL10.GL_BLEND);
}
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
drawPatches(context);
}
private void drawPatches(RenderContext context) {
......@@ -91,34 +85,77 @@ public class NinePatchTileLayer extends TileLayer {
FloatSize size = context.pageSize;
float width = size.width, height = size.height;
drawPatch(context, 0, 0, /* 0 */
drawPatch(context, 0, PATCH_SIZE * 3, /* 0 */
0.0f, 0.0f, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, PATCH_SIZE, 0, /* 1 */
drawPatch(context, PATCH_SIZE, PATCH_SIZE*3, /* 1 */
PATCH_SIZE, 0.0f, width, PATCH_SIZE);
drawPatch(context, PATCH_SIZE * 2, 0, /* 2 */
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE*3, /* 2 */
PATCH_SIZE + width, 0.0f, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, 0, PATCH_SIZE, /* 3 */
drawPatch(context, 0, PATCH_SIZE * 2, /* 3 */
0.0f, PATCH_SIZE, PATCH_SIZE, height);
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE, /* 4 */
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2, /* 4 */
PATCH_SIZE + width, PATCH_SIZE, PATCH_SIZE, height);
drawPatch(context, 0, PATCH_SIZE * 2, /* 5 */
drawPatch(context, 0, PATCH_SIZE, /* 5 */
0.0f, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, PATCH_SIZE, PATCH_SIZE * 2, /* 6 */
drawPatch(context, PATCH_SIZE, PATCH_SIZE, /* 6 */
PATCH_SIZE, PATCH_SIZE + height, width, PATCH_SIZE);
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2, /* 7 */
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE, /* 7 */
PATCH_SIZE + width, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
}
private void drawPatch(RenderContext context, int textureX, int textureY, float tileX,
float tileY, float tileWidth, float tileHeight) {
int[] cropRect = { textureX, textureY + PATCH_SIZE, PATCH_SIZE, -PATCH_SIZE };
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
0);
private void drawPatch(RenderContext context, int textureX, int textureY,
float tileX, float tileY, float tileWidth, float tileHeight) {
RectF viewport = context.viewport;
float viewportHeight = viewport.height();
float drawX = tileX - viewport.left - PATCH_SIZE;
float drawY = viewportHeight - (tileY + tileHeight - viewport.top - PATCH_SIZE);
GLES11Ext.glDrawTexfOES(drawX, drawY, 0.0f, tileWidth, tileHeight);
float[] coords = {
//x, y, z, texture_x, texture_y
drawX/viewport.width(), drawY/viewport.height(), 0,
textureX/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,
drawX/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
textureX/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE,
(drawX+tileWidth)/viewport.width(), drawY/viewport.height(), 0,
(textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,
(drawX+tileWidth)/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
(textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE
};
// 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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
// Use bilinear filtering for both magnification and minimization of the texture. This
// applies only to the shadow layer so we do not incur a high overhead.
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.opengl.GLES20;
import org.libreoffice.DirectBufferAllocator;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public class ScreenshotLayer extends SingleTileLayer {
private static final int SCREENSHOT_SIZE_LIMIT = 1048576;
private ScreenshotImage mImage;
// Size of the image buffer
private IntSize mBufferSize;
// The size of the bitmap painted in the buffer
// (may be smaller than mBufferSize due to power of 2 padding)
private IntSize mImageSize;
// Special case to show the page background color prior to painting a screenshot
private boolean mIsSingleColor = true;
// Force single color, needed for testing
private boolean mForceSingleColor = false;
// Cache the passed in background color to determine if we need to update
// initialized to 0 so it lets the code run to set it to white on init
private int mCurrentBackgroundColor = 0;
public static int getMaxNumPixels() {
return SCREENSHOT_SIZE_LIMIT;
}
public void reset() {
mIsSingleColor = true;
updateBackground(mForceSingleColor, Color.WHITE);
}
void setBitmap(Bitmap bitmap) {
if (mForceSingleColor)
return;
mImageSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
int width = IntSize.nextPowerOfTwo(bitmap.getWidth());
int height = IntSize.nextPowerOfTwo(bitmap.getHeight());
mBufferSize = new IntSize(width, height);
mImage.setBitmap(bitmap, width, height, CairoImage.FORMAT_RGB16_565);
mIsSingleColor = false;
}
public void updateBitmap(Bitmap bitmap, float x, float y, float width, float height) {
mImage.updateBitmap(bitmap, x, y, width, height);
}
public static ScreenshotLayer create() {
// 3 x 3 min for the single color case. Less than 3x3 will blend
// the colors from outside this single color block when scaled
return ScreenshotLayer.create(new IntSize(3, 3));
}
public static ScreenshotLayer create(IntSize size) {
Bitmap bitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.RGB_565);
ScreenshotLayer sl = create(bitmap);
sl.reset();
return sl;
}
public static ScreenshotLayer create(Bitmap bitmap) {
IntSize size = new IntSize(bitmap.getWidth(), bitmap.getHeight());
// allocate a buffer that can hold our max screenshot size
ByteBuffer buffer = DirectBufferAllocator.allocate(SCREENSHOT_SIZE_LIMIT * 2);
// construct the screenshot layer
ScreenshotLayer sl = new ScreenshotLayer(new ScreenshotImage(buffer, size.width, size.height, CairoImage.FORMAT_RGB16_565), size);
// paint the passed in bitmap into the buffer
sl.setBitmap(bitmap);
return sl;
}
private ScreenshotLayer(ScreenshotImage image, IntSize size) {
super(image, TileLayer.PaintMode.STRETCH);
mBufferSize = size;
mImage = image;
}
@Override
public void draw(RenderContext context) {
// mTextureIDs may be null here during startup if Layer.java's draw method
// failed to acquire the transaction lock and call performUpdates.
if (!initialized())
return;
float txl, txr, txb, txt;
if (mIsSingleColor) {
txt = 1.0f;
txr = 0.5f / mBufferSize.width;;
txb = 1.0f - 0.5f / mBufferSize.height;
txl = 0.0f;
} else {
Rect position = getPosition();
float bw = mBufferSize.width;
float bh = mBufferSize.height;
float iw = mImageSize.width;
float ih = mImageSize.height;
float pw = context.pageSize.width;
float ph = context.pageSize.height;
float vl = context.viewport.left;
float vr = context.viewport.right;
float vt = context.viewport.top;
float vb = context.viewport.bottom;
float vw = vr - vl;
float vh = vb - vt;
txl = (iw/bw) * (vl / pw);
txr = (iw/bw) * (vr / pw);
txt = 1.0f - ((ih/bh) * (vt / ph));
txb = 1.0f - ((ih/bh) * (vb / ph));
}
float[] coords = {
0.0f, 0.0f, 0.0f, txl, txb,
0.0f, 1.0f, 0.0f, txl, txt,
1.0f, 0.0f, 0.0f, txr, txb,
1.0f, 1.0f, 0.0f, txr, txt,
};
FloatBuffer coordBuffer = context.coordBuffer;
int positionHandle = context.positionHandle;
int textureHandle = context.textureHandle;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
// Make sure we are at position zero in the buffer
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);
}
public boolean updateBackground(boolean showChecks, int color) {
if (!showChecks) {
mIsSingleColor = true;
mForceSingleColor = true;
} else {
mForceSingleColor = false;
}
if (!mIsSingleColor || color == mCurrentBackgroundColor)
return false;
mCurrentBackgroundColor = color;
/* mask each component of the 8888 color and bit shift to least
* sigificant. Then for red and blue multiply by (2^5 -1) and (2^6 - 1)
* for green. Finally, divide by (2^8 - 1) for all color values. This
* scales the 8 bit color values to 5 or 6 bits
*/
int red = ((color & 0x00FF0000 >> 16)* 31 / 255);
int green = ((color & 0x0000FF00 >> 8) * 63 / 255);
int blue = (color & 0x000000FF) * 31 / 255;
/* For the first byte left shift red by 3 positions such that it is the
* top 5 bits, right shift green by 3 so its 3 most significant are the
* 3 least significant. For the second byte, left shift green by 3 so
* its 3 least significant bits are the 3 most significant bits of the
* byte. Finally, set the 5 least significant bits to blue's value.
*/
byte byte1 = (byte)((red << 3 | green >> 3) & 0x0000FFFF);
byte byte2 = (byte)((green << 5 | blue) & 0x0000FFFF);
mImage.mBuffer.put(1, byte1);
mImage.mBuffer.put(0, byte2);
mImage.mBuffer.put(3, byte1);
mImage.mBuffer.put(2, byte2);
mImage.mBuffer.put(5, byte1);
mImage.mBuffer.put(4, byte2);
mImage.mBuffer.put(mImageSize.width + 1, byte1);
mImage.mBuffer.put(mImageSize.width + 0, byte2);
mImage.mBuffer.put(mImageSize.width + 3, byte1);
mImage.mBuffer.put(mImageSize.width + 2, byte2);
mImage.mBuffer.put(mImageSize.width + 5, byte1);
mImage.mBuffer.put(mImageSize.width + 4, byte2);
return true;
}
/** A Cairo image that simply saves a buffer of pixel data. */
static class ScreenshotImage extends CairoImage {
ByteBuffer mBuffer;
private IntSize mSize;
private int mFormat;
/** Creates a buffered Cairo image from a byte buffer. */
public ScreenshotImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
mBuffer = inBuffer; mSize = new IntSize(inWidth, inHeight); mFormat = inFormat;
}
@Override
protected void finalize() throws Throwable {
try {
if (mBuffer != null)
DirectBufferAllocator.free(mBuffer);
} finally {
super.finalize();
}
}
void setBitmap(Bitmap bitmap, int width, int height, int format) {
Bitmap tmp;
mSize = new IntSize(width, height);
mFormat = format;
if (width == bitmap.getWidth() && height == bitmap.getHeight()) {
tmp = bitmap;
} else {
tmp = Bitmap.createBitmap(width, height, CairoUtils.cairoFormatTobitmapConfig(mFormat));
new Canvas(tmp).drawBitmap(bitmap, 0.0f, 0.0f, new Paint());
}
tmp.copyPixelsToBuffer(mBuffer.asIntBuffer());
}
public void updateBitmap(Bitmap bitmap, float x, float y, float width, float height) {
Bitmap tmp = Bitmap.createBitmap(mSize.width, mSize.height, CairoUtils.cairoFormatTobitmapConfig(mFormat));
tmp.copyPixelsFromBuffer(mBuffer.asIntBuffer());
Canvas c = new Canvas(tmp);
c.drawBitmap(bitmap, x, y, new Paint());
tmp.copyPixelsToBuffer(mBuffer.asIntBuffer());
}
@Override
public ByteBuffer getBuffer() { return mBuffer; }
@Override
public void destroy() {
}
@Override
public IntSize getSize() { return mSize; }
@Override
public int getFormat() { return mFormat; }
}
}
......@@ -38,17 +38,13 @@
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.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.opengl.GLES20;
import android.util.Log;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
/**
* Encapsulates the logic needed to draw a single textured tile.
......@@ -56,10 +52,25 @@ import javax.microedition.khronos.opengles.GL10;
* TODO: Repeating textures really should be their own type of layer.
*/
public class SingleTileLayer extends TileLayer {
private static final String LOGTAG = "GeckoSingleTileLayer";
private Rect mMask;
public SingleTileLayer(CairoImage image) { this(false, image); }
public SingleTileLayer(boolean repeat, CairoImage image) {
super(repeat, image);
super(image, repeat ? PaintMode.REPEAT : PaintMode.NORMAL);
}
public SingleTileLayer(CairoImage image, TileLayer.PaintMode paintMode) {
super(image, paintMode);
}
/**
* Set an area to mask out when rendering.
*/
public void setMask(Rect aMaskRect) {
mMask = aMaskRect;
}
@Override
......@@ -69,59 +80,83 @@ public class SingleTileLayer extends TileLayer {
if (!initialized())
return;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
RectF bounds;
int[] cropRect;
IntSize size = getSize();
Rect position = getPosition();
RectF viewport = context.viewport;
if (repeats()) {
if (repeats() || stretches()) {
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, 0, width, height };
} else {
bounds = getBounds(context, new FloatSize(size));
cropRect = new int[] { 0, 0, size.width, size.height };
bounds = getBounds(context);
}
float height = bounds.height();
float left = bounds.left - viewport.left;
float top = viewport.height() - (bounds.top + height - viewport.top);
Rect intBounds = new Rect();
bounds.roundOut(intBounds);
Region maskedBounds = new Region(intBounds);
if (mMask != null) {
maskedBounds.op(mMask, Region.Op.DIFFERENCE);
if (maskedBounds.isEmpty())
return;
}
float[] coords = {
// XXX Possible optimisation here, form this array so we can draw it in
// a single call.
RegionIterator i = new RegionIterator(maskedBounds);
for (Rect subRect = new Rect(); i.next(subRect);) {
// Compensate for rounding errors at the edge of the tile caused by
// the roundOut above
RectF subRectF = new RectF(Math.max(bounds.left, (float)subRect.left),
Math.max(bounds.top, (float)subRect.top),
Math.min(bounds.right, (float)subRect.right),
Math.min(bounds.bottom, (float)subRect.bottom));
int[] cropRect = new int[] { Math.round(subRectF.left - bounds.left),
Math.round(subRectF.bottom - bounds.top),
Math.round(subRectF.right - bounds.left),
Math.round(subRectF.top - bounds.top) };
cropRect = new int[] { 0, 0, position.width(), position.height() };
float height = subRectF.height();
float left = subRectF.left - viewport.left;
float top = viewport.height() - (subRectF.top + height - viewport.top);
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,
cropRect[0]/(float)position.width(), cropRect[1]/(float)position.height(),
left/viewport.width(), (top+height)/viewport.height(), 0,
cropRect[0]/(float)size.width, cropRect[3]/(float)size.height,
cropRect[0]/(float)position.width(), cropRect[3]/(float)position.height(),
(left+bounds.width())/viewport.width(), top/viewport.height(), 0,
cropRect[2]/(float)size.width, cropRect[1]/(float)size.height,
(left+subRectF.width())/viewport.width(), top/viewport.height(), 0,
cropRect[2]/(float)position.width(), cropRect[1]/(float)position.height(),
(left+bounds.width())/viewport.width(), (top+height)/viewport.height(), 0,
cropRect[2]/(float)size.width, cropRect[3]/(float)size.height
};
(left+subRectF.width())/viewport.width(), (top+height)/viewport.height(), 0,
cropRect[2]/(float)position.width(), cropRect[3]/(float)position.height()
};
FloatBuffer coordBuffer = context.coordBuffer;
int positionHandle = context.positionHandle;
int textureHandle = context.textureHandle;
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);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
// 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);
// Make sure we are at position zero in the buffer
coordBuffer.position(0);
coordBuffer.put(coords);
// 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);
// 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);
}
}
}
......@@ -40,9 +40,7 @@ package org.mozilla.gecko.gfx;
import android.graphics.Rect;
import android.opengl.GLES20;
import android.util.Log;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
......@@ -54,53 +52,50 @@ public abstract class TileLayer extends Layer {
private final Rect mDirtyRect;
private final CairoImage mImage;
private final boolean mRepeat;
private IntSize mSize;
private boolean mSkipTextureUpdate;
private int[] mTextureIDs;
public TileLayer(boolean repeat, CairoImage image) {
mRepeat = repeat;
public enum PaintMode { NORMAL, REPEAT, STRETCH };
private PaintMode mPaintMode;
public TileLayer(CairoImage image, PaintMode paintMode) {
super(image.getSize());
mPaintMode = paintMode;
mImage = image;
mSize = new IntSize(0, 0);
mSkipTextureUpdate = false;
IntSize bufferSize = mImage.getSize();
mDirtyRect = new Rect();
}
@Override
public IntSize getSize() { return mImage.getSize(); }
protected boolean repeats() { return mRepeat; }
protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; }
protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; }
protected int getTextureID() { return mTextureIDs[0]; }
protected boolean initialized() { return mImage != null && mTextureIDs != null; }
@Override
protected void finalize() throws Throwable {
if (mTextureIDs != null) {
if (mTextureIDs != null)
TextureReaper.get().add(mTextureIDs);
}
@Override
public void setPosition(Rect newPosition) {
if (newPosition.width() != mImage.getSize().width || newPosition.height() != mImage.getSize().height) {
throw new RuntimeException("Error: changing the size of a tile layer is not allowed!");
}
mImage.destroy();
super.setPosition(newPosition);
}
/**
* Invalidates the given rect so that it will be uploaded again. Only valid inside a
* Invalidates the entire buffer so that it will be uploaded again. Only valid inside a
* transaction.
*/
public void invalidate(Rect rect) {
if (!inTransaction())
throw new RuntimeException("invalidate() is only valid inside a transaction");
mDirtyRect.union(rect);
}
public void invalidate() {
if (!inTransaction())
throw new RuntimeException("invalidate() is only valid inside a transaction");
IntSize bufferSize = mImage.getSize();
invalidate(new Rect(0, 0, bufferSize.width, bufferSize.height));
}
public boolean isDirty() {
return mImage.getSize().isPositive() && (mTextureIDs == null || !mDirtyRect.isEmpty());
mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height);
}
private void validateTexture() {
......@@ -131,29 +126,16 @@ public abstract class TileLayer extends Layer {
}
}
/** Tells the tile not to update the texture on the next update. */
public void setSkipTextureUpdate(boolean skip) {
mSkipTextureUpdate = skip;
}
public boolean getSkipTextureUpdate() {
return mSkipTextureUpdate;
}
@Override
protected boolean performUpdates(RenderContext context) {
protected void performUpdates(RenderContext context) {
super.performUpdates(context);
if (mSkipTextureUpdate) {
return false;
}
// Reallocate the texture if the size has changed
validateTexture();
// Don't do any work if the image has an invalid size.
if (!mImage.getSize().isPositive())
return true;
return;
// If we haven't allocated a texture, assume the whole region is dirty
if (mTextureIDs == null) {
......@@ -163,8 +145,6 @@ public abstract class TileLayer extends Layer {
}
mDirtyRect.setEmpty();
return true;
}
private void uploadFullTexture() {
......@@ -182,71 +162,28 @@ public abstract class TileLayer extends Layer {
if (imageBuffer == null)
return;
boolean newlyCreated = false;
if (mTextureIDs == null) {
mTextureIDs = new int[1];
GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
newlyCreated = true;
}
IntSize bufferSize = mImage.getSize();
Rect bufferRect = new Rect(0, 0, bufferSize.width, bufferSize.height);
int cairoFormat = mImage.getFormat();
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
bindAndSetGLParameters();
if (newlyCreated || dirtyRect.contains(bufferRect)) {
if (mSize.equals(bufferSize)) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width,
mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
return;
} else {
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)) {
return;
}
/*
* Upload the changed rect. We have to widen to the full width of the texture
* because we can't count on the device having support for GL_EXT_unpack_subimage,
* and going line-by-line is too slow.
*
* XXX We should still use GL_EXT_unpack_subimage when available.
*/
Buffer viewBuffer = imageBuffer.slice();
int bpp = CairoUtils.bitsPerPixelForCairoFormat(cairoFormat) / 8;
int position = dirtyRect.top * bufferSize.width * bpp;
if (position > viewBuffer.limit()) {
Log.e(LOGTAG, "### Position outside tile! " + dirtyRect.top);
return;
}
viewBuffer.position(position);
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);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width,
mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
}
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.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.GL_LINEAR);
int repeatMode = mRepeat ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE;
int repeatMode = repeats() ? 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);
}
......
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko.gfx;
import 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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment