зеркало из https://github.com/mozilla/pjs.git
Bug 708307 - Decouple texture size from tile size. r=pcwalton a=android-only
This removes the hard-coded limit of 1024x2048 tile sizes, and allows for arbitrary tile-sizes. It will still only allocate texture sizes in powers of two, however. It replaces the tile size with a buffered-area size, which can be re-allocated as the screen dimensions change.
This commit is contained in:
Родитель
4e929653f5
Коммит
660420952d
|
@ -491,15 +491,6 @@ public class GeckoAppShell
|
||||||
/*
|
/*
|
||||||
* The Gecko-side API: API methods that Gecko calls
|
* The Gecko-side API: API methods that Gecko calls
|
||||||
*/
|
*/
|
||||||
public static void scheduleRedraw() {
|
|
||||||
// Redraw everything
|
|
||||||
Rect rect = new Rect(0, 0, LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
|
|
||||||
GeckoEvent event = new GeckoEvent(GeckoEvent.DRAW, rect);
|
|
||||||
event.mNativeWindow = 0;
|
|
||||||
sendEventToGecko(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void notifyIME(int type, int state) {
|
public static void notifyIME(int type, int state) {
|
||||||
mInputConnection.notifyIME(type, state);
|
mInputConnection.notifyIME(type, state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,21 +46,22 @@ import java.nio.ByteBuffer;
|
||||||
/** A Cairo image that simply saves a buffer of pixel data. */
|
/** A Cairo image that simply saves a buffer of pixel data. */
|
||||||
public class BufferedCairoImage extends CairoImage {
|
public class BufferedCairoImage extends CairoImage {
|
||||||
private ByteBuffer mBuffer;
|
private ByteBuffer mBuffer;
|
||||||
private int mWidth, mHeight, mFormat;
|
private IntSize mSize;
|
||||||
|
private int mFormat;
|
||||||
private boolean mNeedToFreeBuffer = false;
|
private boolean mNeedToFreeBuffer = false;
|
||||||
|
|
||||||
/** Creates a buffered Cairo image from a byte buffer. */
|
/** Creates a buffered Cairo image from a byte buffer. */
|
||||||
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
||||||
mBuffer = inBuffer; mWidth = inWidth; mHeight = inHeight; mFormat = inFormat;
|
mBuffer = inBuffer; mSize = new IntSize(inWidth, inHeight); mFormat = inFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a buffered Cairo image from an Android bitmap. */
|
/** Creates a buffered Cairo image from an Android bitmap. */
|
||||||
public BufferedCairoImage(Bitmap bitmap) {
|
public BufferedCairoImage(Bitmap bitmap) {
|
||||||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||||
mWidth = bitmap.getWidth();
|
mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
|
||||||
mHeight = bitmap.getHeight();
|
|
||||||
mNeedToFreeBuffer = true;
|
mNeedToFreeBuffer = true;
|
||||||
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 4);
|
// XXX Why is this * 4? Shouldn't it depend on mFormat?
|
||||||
|
mBuffer = GeckoAppShell.allocateDirectBuffer(mSize.getArea() * 4);
|
||||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +79,7 @@ public class BufferedCairoImage extends CairoImage {
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getBuffer() { return mBuffer; }
|
public ByteBuffer getBuffer() { return mBuffer; }
|
||||||
@Override
|
@Override
|
||||||
public int getWidth() { return mWidth; }
|
public IntSize getSize() { return mSize; }
|
||||||
@Override
|
|
||||||
public int getHeight() { return mHeight; }
|
|
||||||
@Override
|
@Override
|
||||||
public int getFormat() { return mFormat; }
|
public int getFormat() { return mFormat; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,7 @@ import java.nio.ByteBuffer;
|
||||||
public abstract class CairoImage {
|
public abstract class CairoImage {
|
||||||
public abstract ByteBuffer getBuffer();
|
public abstract ByteBuffer getBuffer();
|
||||||
|
|
||||||
public abstract int getWidth();
|
public abstract IntSize getSize();
|
||||||
public abstract int getHeight();
|
|
||||||
public abstract int getFormat();
|
public abstract int getFormat();
|
||||||
|
|
||||||
public static final int FORMAT_INVALID = -1;
|
public static final int FORMAT_INVALID = -1;
|
||||||
|
|
|
@ -72,8 +72,9 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
private static final String LOGTAG = "GeckoSoftwareLayerClient";
|
private static final String LOGTAG = "GeckoSoftwareLayerClient";
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private int mWidth, mHeight, mFormat;
|
private int mFormat;
|
||||||
private IntSize mScreenSize, mViewportSize;
|
private IntSize mScreenSize, mViewportSize;
|
||||||
|
private IntSize mBufferSize;
|
||||||
private ByteBuffer mBuffer;
|
private ByteBuffer mBuffer;
|
||||||
private final SingleTileLayer mTileLayer;
|
private final SingleTileLayer mTileLayer;
|
||||||
|
|
||||||
|
@ -97,21 +98,15 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
public GeckoSoftwareLayerClient(Context context) {
|
public GeckoSoftwareLayerClient(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
|
||||||
mWidth = LayerController.TILE_WIDTH;
|
mScreenSize = new IntSize(0, 0);
|
||||||
mHeight = LayerController.TILE_HEIGHT;
|
mBufferSize = new IntSize(0, 0);
|
||||||
mFormat = CairoImage.FORMAT_RGB16_565;
|
mFormat = CairoImage.FORMAT_RGB16_565;
|
||||||
|
|
||||||
mScreenSize = new IntSize(1, 1);
|
|
||||||
|
|
||||||
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 2);
|
|
||||||
|
|
||||||
mCairoImage = new CairoImage() {
|
mCairoImage = new CairoImage() {
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getBuffer() { return mBuffer; }
|
public ByteBuffer getBuffer() { return mBuffer; }
|
||||||
@Override
|
@Override
|
||||||
public int getWidth() { return mWidth; }
|
public IntSize getSize() { return mBufferSize; }
|
||||||
@Override
|
|
||||||
public int getHeight() { return mHeight; }
|
|
||||||
@Override
|
@Override
|
||||||
public int getFormat() { return mFormat; }
|
public int getFormat() { return mFormat; }
|
||||||
};
|
};
|
||||||
|
@ -141,7 +136,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
layerController.notifyPanZoomControllerOfGeometryChange(false);
|
layerController.notifyPanZoomControllerOfGeometryChange(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
geometryChanged();
|
|
||||||
GeckoAppShell.registerGeckoEventListener("Viewport:Update", this);
|
GeckoAppShell.registerGeckoEventListener("Viewport:Update", this);
|
||||||
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
|
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +203,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
|
|
||||||
public Bitmap getBitmap() {
|
public Bitmap getBitmap() {
|
||||||
try {
|
try {
|
||||||
Bitmap b = Bitmap.createBitmap(mWidth, mHeight,
|
Bitmap b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height,
|
||||||
CairoUtils.cairoFormatTobitmapConfig(mFormat));
|
CairoUtils.cairoFormatTobitmapConfig(mFormat));
|
||||||
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
|
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
|
||||||
return b;
|
return b;
|
||||||
|
@ -241,9 +235,28 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
if (metrics.widthPixels != mScreenSize.width ||
|
if (metrics.widthPixels != mScreenSize.width ||
|
||||||
metrics.heightPixels != mScreenSize.height) {
|
metrics.heightPixels != mScreenSize.height) {
|
||||||
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
|
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
|
||||||
|
int maxSize = getLayerController().getView().getMaxTextureSize();
|
||||||
|
|
||||||
|
// XXX Introduce tiling to solve 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 use NPOT texture support
|
||||||
|
mBufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
|
||||||
|
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
|
||||||
|
|
||||||
|
// Free the old buffer first, if it exists
|
||||||
|
if (mBuffer != null) {
|
||||||
|
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||||
|
mBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// * 2 because it's a 16-bit buffer (so 2 bytes per pixel).
|
||||||
|
mBuffer = GeckoAppShell.allocateDirectBuffer(mBufferSize.getArea() * 2);
|
||||||
|
|
||||||
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
|
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
|
||||||
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
|
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
|
||||||
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT,
|
mBufferSize.width, mBufferSize.height,
|
||||||
metrics.widthPixels, metrics.heightPixels);
|
metrics.widthPixels, metrics.heightPixels);
|
||||||
GeckoAppShell.sendEventToGecko(event);
|
GeckoAppShell.sendEventToGecko(event);
|
||||||
}
|
}
|
||||||
|
@ -289,7 +302,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
ViewportMetrics viewportMetrics =
|
ViewportMetrics viewportMetrics =
|
||||||
new ViewportMetrics(getLayerController().getViewportMetrics());
|
new ViewportMetrics(getLayerController().getViewportMetrics());
|
||||||
|
|
||||||
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset();
|
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(mBufferSize);
|
||||||
viewportMetrics.setViewportOffset(viewportOffset);
|
viewportMetrics.setViewportOffset(viewportOffset);
|
||||||
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
|
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
|
||||||
|
|
||||||
|
|
|
@ -62,10 +62,18 @@ public class IntSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getArea() {
|
||||||
|
return width * height;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean equals(IntSize size) {
|
public boolean equals(IntSize size) {
|
||||||
return ((size.width == width) && (size.height == height));
|
return ((size.width == width) && (size.height == height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPositive() {
|
||||||
|
return (width > 0 && height > 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() { return "(" + width + "," + height + ")"; }
|
public String toString() { return "(" + width + "," + height + ")"; }
|
||||||
|
|
||||||
|
@ -73,5 +81,23 @@ public class IntSize {
|
||||||
return new IntSize((int)Math.round(width * factor),
|
return new IntSize((int)Math.round(width * factor),
|
||||||
(int)Math.round(height * factor));
|
(int)Math.round(height * factor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the power of two that is greater than or equal to value */
|
||||||
|
public static int nextPowerOfTwo(int value) {
|
||||||
|
// code taken from http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html
|
||||||
|
if (0 == value--) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
value = (value >> 1) | value;
|
||||||
|
value = (value >> 2) | value;
|
||||||
|
value = (value >> 4) | value;
|
||||||
|
value = (value >> 8) | value;
|
||||||
|
value = (value >> 16) | value;
|
||||||
|
return value + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntSize nextPowerOfTwo() {
|
||||||
|
return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,9 @@ public abstract class Layer {
|
||||||
/** Subclasses override this function to draw the layer. */
|
/** Subclasses override this function to draw the layer. */
|
||||||
public abstract void draw(RenderContext context);
|
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. */
|
/** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */
|
||||||
protected RectF getBounds(RenderContext context, FloatSize size) {
|
protected RectF getBounds(RenderContext context, FloatSize size) {
|
||||||
float scaleFactor = context.zoomFactor / mResolution;
|
float scaleFactor = context.zoomFactor / mResolution;
|
||||||
|
@ -168,20 +171,6 @@ public abstract class Layer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the power of two that is greater than or equal to value */
|
|
||||||
protected static int nextPowerOfTwo(int value) {
|
|
||||||
// code taken from http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html
|
|
||||||
if (0 == value--) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
value = (value >> 1) | value;
|
|
||||||
value = (value >> 2) | value;
|
|
||||||
value = (value >> 4) | value;
|
|
||||||
value = (value >> 8) | value;
|
|
||||||
value = (value >> 16) | value;
|
|
||||||
return value + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RenderContext {
|
public static class RenderContext {
|
||||||
public final RectF viewport;
|
public final RectF viewport;
|
||||||
public final FloatSize pageSize;
|
public final FloatSize pageSize;
|
||||||
|
|
|
@ -85,9 +85,11 @@ public class LayerController {
|
||||||
|
|
||||||
private boolean mForceRedraw;
|
private boolean mForceRedraw;
|
||||||
|
|
||||||
/* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */
|
/* The extra area on the sides of the page that we want to buffer to help with
|
||||||
public static final int TILE_WIDTH = 1024;
|
* smooth, asynchronous scrolling. Depending on a device's support for NPOT
|
||||||
public static final int TILE_HEIGHT = 2048;
|
* textures, this may be rounded up to the nearest power of two.
|
||||||
|
*/
|
||||||
|
public static final IntSize MIN_BUFFER = new IntSize(512, 1024);
|
||||||
|
|
||||||
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
|
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
|
||||||
* we start aggressively redrawing to minimize checkerboarding. */
|
* we start aggressively redrawing to minimize checkerboarding. */
|
||||||
|
@ -293,8 +295,12 @@ public class LayerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private RectF getTileRect() {
|
private RectF getTileRect() {
|
||||||
|
if (mRootLayer == null)
|
||||||
|
return new RectF();
|
||||||
|
|
||||||
float x = mRootLayer.getOrigin().x, y = mRootLayer.getOrigin().y;
|
float x = mRootLayer.getOrigin().x, y = mRootLayer.getOrigin().y;
|
||||||
return new RectF(x, y, x + TILE_WIDTH, y + TILE_HEIGHT);
|
IntSize layerSize = mRootLayer.getSize();
|
||||||
|
return new RectF(x, y, x + layerSize.width, y + layerSize.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RectF restrictToPageSize(RectF aRect) {
|
public RectF restrictToPageSize(RectF aRect) {
|
||||||
|
|
|
@ -90,6 +90,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||||
private final ScrollbarLayer mVertScrollLayer;
|
private final ScrollbarLayer mVertScrollLayer;
|
||||||
private final FadeRunnable mFadeRunnable;
|
private final FadeRunnable mFadeRunnable;
|
||||||
private RenderContext mLastPageContext;
|
private RenderContext mLastPageContext;
|
||||||
|
private int mMaxTextureSize;
|
||||||
|
|
||||||
// Dropped frames display
|
// Dropped frames display
|
||||||
private int[] mFrameTimings;
|
private int[] mFrameTimings;
|
||||||
|
@ -122,12 +123,17 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||||
checkFrameRateMonitorEnabled();
|
checkFrameRateMonitorEnabled();
|
||||||
|
|
||||||
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
gl.glClearDepthf(1.0f); /* FIXME: Is this needed? */
|
|
||||||
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
||||||
gl.glShadeModel(GL10.GL_SMOOTH); /* FIXME: Is this needed? */
|
|
||||||
gl.glDisable(GL10.GL_DITHER);
|
gl.glDisable(GL10.GL_DITHER);
|
||||||
gl.glEnable(GL10.GL_TEXTURE_2D);
|
gl.glEnable(GL10.GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
int maxTextureSizeResult[] = new int[1];
|
||||||
|
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
|
||||||
|
mMaxTextureSize = maxTextureSizeResult[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxTextureSize() {
|
||||||
|
return mMaxTextureSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -167,5 +167,9 @@ public class LayerView extends GLSurfaceView {
|
||||||
return System.nanoTime() - mRenderTime;
|
return System.nanoTime() - mRenderTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMaxTextureSize() {
|
||||||
|
return mRenderer.getMaxTextureSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,8 @@ public class ScrollbarLayer extends TileLayer {
|
||||||
mVertical = vertical;
|
mVertical = vertical;
|
||||||
mBuffer = buffer;
|
mBuffer = buffer;
|
||||||
|
|
||||||
mBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
|
IntSize size = image.getSize();
|
||||||
|
mBitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888);
|
||||||
mCanvas = new Canvas(mBitmap);
|
mCanvas = new Canvas(mBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +101,7 @@ public class ScrollbarLayer extends TileLayer {
|
||||||
public static ScrollbarLayer create(boolean vertical) {
|
public static ScrollbarLayer create(boolean vertical) {
|
||||||
// just create an empty image for now, it will get drawn
|
// just create an empty image for now, it will get drawn
|
||||||
// on demand anyway
|
// on demand anyway
|
||||||
int imageSize = nextPowerOfTwo(BAR_SIZE);
|
int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE);
|
||||||
ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(imageSize * imageSize * 4);
|
ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(imageSize * imageSize * 4);
|
||||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
||||||
return new ScrollbarLayer(image, vertical, buffer);
|
return new ScrollbarLayer(image, vertical, buffer);
|
||||||
|
|
|
@ -39,6 +39,7 @@ package org.mozilla.gecko.gfx;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
|
import android.opengl.GLES20;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import javax.microedition.khronos.opengles.GL11Ext;
|
import javax.microedition.khronos.opengles.GL11Ext;
|
||||||
|
@ -58,27 +59,18 @@ public abstract class TileLayer extends Layer {
|
||||||
private final ArrayList<Rect> mDirtyRects;
|
private final ArrayList<Rect> mDirtyRects;
|
||||||
private final CairoImage mImage;
|
private final CairoImage mImage;
|
||||||
private final boolean mRepeat;
|
private final boolean mRepeat;
|
||||||
private final IntSize mSize;
|
private IntSize mSize;
|
||||||
private int[] mTextureIDs;
|
private int[] mTextureIDs;
|
||||||
|
|
||||||
public TileLayer(boolean repeat, CairoImage image) {
|
public TileLayer(boolean repeat, CairoImage image) {
|
||||||
mRepeat = repeat;
|
mRepeat = repeat;
|
||||||
mImage = image;
|
mImage = image;
|
||||||
mSize = new IntSize(image.getWidth(), image.getHeight());
|
mSize = new IntSize(0, 0);
|
||||||
mDirtyRects = new ArrayList<Rect>();
|
mDirtyRects = new ArrayList<Rect>();
|
||||||
|
|
||||||
/*
|
|
||||||
* Assert that the image has a power-of-two size. OpenGL ES < 2.0 doesn't support NPOT
|
|
||||||
* textures and OpenGL ES doesn't seem to let us efficiently slice up a NPOT bitmap.
|
|
||||||
*/
|
|
||||||
int width = mImage.getWidth(), height = mImage.getHeight();
|
|
||||||
if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0) {
|
|
||||||
throw new RuntimeException("TileLayer: NPOT images are unsupported (dimensions are " +
|
|
||||||
width + "x" + height + ")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntSize getSize() { return mSize; }
|
@Override
|
||||||
|
public IntSize getSize() { return mImage.getSize(); }
|
||||||
|
|
||||||
protected boolean repeats() { return mRepeat; }
|
protected boolean repeats() { return mRepeat; }
|
||||||
protected int getTextureID() { return mTextureIDs[0]; }
|
protected int getTextureID() { return mTextureIDs[0]; }
|
||||||
|
@ -101,13 +93,49 @@ public abstract class TileLayer extends Layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
invalidate(new Rect(0, 0, mSize.width, mSize.height));
|
IntSize bufferSize = mImage.getSize();
|
||||||
|
invalidate(new Rect(0, 0, bufferSize.width, bufferSize.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
* extensions), but doesn't support repeating NPOT textures.
|
||||||
|
*
|
||||||
|
* XXX Currently, we don't pick a GLES 2.0 context, so always round.
|
||||||
|
*/
|
||||||
|
IntSize bufferSize = mImage.getSize();
|
||||||
|
IntSize textureSize = bufferSize;
|
||||||
|
|
||||||
|
textureSize = bufferSize.nextPowerOfTwo();
|
||||||
|
|
||||||
|
if (!textureSize.equals(mSize)) {
|
||||||
|
mSize = textureSize;
|
||||||
|
|
||||||
|
// Delete the old texture
|
||||||
|
if (mTextureIDs != null) {
|
||||||
|
TextureReaper.get().add(mTextureIDs);
|
||||||
|
mTextureIDs = null;
|
||||||
|
|
||||||
|
// XXX This won't be freed until the next frame is drawn, so we
|
||||||
|
// temporarily have a larger-than-necessary memory requirement.
|
||||||
|
// Is this what we want?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void performUpdates(GL10 gl) {
|
protected void performUpdates(GL10 gl) {
|
||||||
super.performUpdates(gl);
|
super.performUpdates(gl);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
if (mTextureIDs == null) {
|
if (mTextureIDs == null) {
|
||||||
uploadFullTexture(gl);
|
uploadFullTexture(gl);
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,43 +147,58 @@ public abstract class TileLayer extends Layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadFullTexture(GL10 gl) {
|
private void uploadFullTexture(GL10 gl) {
|
||||||
mTextureIDs = new int[1];
|
IntSize bufferSize = mImage.getSize();
|
||||||
gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
|
uploadDirtyRect(gl, new Rect(0, 0, bufferSize.width, bufferSize.height));
|
||||||
|
|
||||||
int cairoFormat = mImage.getFormat();
|
|
||||||
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
|
|
||||||
|
|
||||||
bindAndSetGLParameters(gl);
|
|
||||||
|
|
||||||
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height,
|
|
||||||
0, glInfo.format, glInfo.type, mImage.getBuffer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadDirtyRect(GL10 gl, Rect dirtyRect) {
|
private void uploadDirtyRect(GL10 gl, Rect dirtyRect) {
|
||||||
if (mTextureIDs == null)
|
boolean newlyCreated = false;
|
||||||
throw new RuntimeException("uploadDirtyRect() called with null texture ID!");
|
|
||||||
|
if (mTextureIDs == null) {
|
||||||
|
mTextureIDs = new int[1];
|
||||||
|
gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
|
||||||
|
newlyCreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntSize bufferSize = mImage.getSize();
|
||||||
|
Rect bufferRect = new Rect(0, 0, bufferSize.width, bufferSize.height);
|
||||||
|
|
||||||
int width = mSize.width;
|
|
||||||
int cairoFormat = mImage.getFormat();
|
int cairoFormat = mImage.getFormat();
|
||||||
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
|
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
|
||||||
|
|
||||||
bindAndSetGLParameters(gl);
|
bindAndSetGLParameters(gl);
|
||||||
|
|
||||||
|
if (newlyCreated || dirtyRect.equals(bufferRect)) {
|
||||||
|
if (mSize.equals(bufferSize)) {
|
||||||
|
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height,
|
||||||
|
0, glInfo.format, glInfo.type, mImage.getBuffer());
|
||||||
|
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, mImage.getBuffer());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload the changed rect. We have to widen to the full width of the texture
|
* 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,
|
* because we can't count on the device having support for GL_EXT_unpack_subimage,
|
||||||
* and going line-by-line is too slow.
|
* and going line-by-line is too slow.
|
||||||
|
*
|
||||||
|
* XXX We should still use GL_EXT_unpack_subimage when available.
|
||||||
*/
|
*/
|
||||||
Buffer viewBuffer = mImage.getBuffer().slice();
|
Buffer viewBuffer = mImage.getBuffer().slice();
|
||||||
int bpp = CairoUtils.bitsPerPixelForCairoFormat(cairoFormat) / 8;
|
int bpp = CairoUtils.bitsPerPixelForCairoFormat(cairoFormat) / 8;
|
||||||
int position = dirtyRect.top * width * bpp;
|
int position = dirtyRect.top * bufferSize.width * bpp;
|
||||||
if (position > viewBuffer.limit()) {
|
if (position > viewBuffer.limit()) {
|
||||||
Log.e(LOGTAG, "### Position outside tile! " + dirtyRect.top);
|
Log.e(LOGTAG, "### Position outside tile! " + dirtyRect.top);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBuffer.position(position);
|
viewBuffer.position(position);
|
||||||
gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, width, dirtyRect.height(),
|
gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, dirtyRect.height(),
|
||||||
glInfo.format, glInfo.type, viewBuffer);
|
glInfo.format, glInfo.type, viewBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import org.mozilla.gecko.FloatUtils;
|
import org.mozilla.gecko.FloatUtils;
|
||||||
import org.mozilla.gecko.gfx.FloatSize;
|
import org.mozilla.gecko.gfx.FloatSize;
|
||||||
|
import org.mozilla.gecko.gfx.IntSize;
|
||||||
import org.mozilla.gecko.gfx.LayerController;
|
import org.mozilla.gecko.gfx.LayerController;
|
||||||
import org.mozilla.gecko.gfx.RectUtils;
|
import org.mozilla.gecko.gfx.RectUtils;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -64,8 +65,7 @@ public class ViewportMetrics {
|
||||||
private float mZoomFactor;
|
private float mZoomFactor;
|
||||||
|
|
||||||
public ViewportMetrics() {
|
public ViewportMetrics() {
|
||||||
mPageSize = new FloatSize(LayerController.TILE_WIDTH,
|
mPageSize = new FloatSize(1, 1);
|
||||||
LayerController.TILE_HEIGHT);
|
|
||||||
mViewportRect = new RectF(0, 0, 1, 1);
|
mViewportRect = new RectF(0, 0, 1, 1);
|
||||||
mViewportOffset = new PointF(0, 0);
|
mViewportOffset = new PointF(0, 0);
|
||||||
mZoomFactor = 1.0f;
|
mZoomFactor = 1.0f;
|
||||||
|
@ -96,13 +96,13 @@ public class ViewportMetrics {
|
||||||
mZoomFactor = zoom;
|
mZoomFactor = zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PointF getOptimumViewportOffset() {
|
public PointF getOptimumViewportOffset(IntSize displayportSize) {
|
||||||
// XXX We currently always position the viewport in the centre of the
|
// XXX We currently always position the viewport in the centre of the
|
||||||
// displayport, but we might want to optimise this during panning
|
// displayport, but we might want to optimise this during panning
|
||||||
// to minimise checkerboarding.
|
// to minimise checkerboarding.
|
||||||
Point optimumOffset =
|
Point optimumOffset =
|
||||||
new Point((int)Math.round((LayerController.TILE_WIDTH - mViewportRect.width()) / 2),
|
new Point((int)Math.round((displayportSize.width - mViewportRect.width()) / 2),
|
||||||
(int)Math.round((LayerController.TILE_HEIGHT - mViewportRect.height()) / 2));
|
(int)Math.round((displayportSize.height - mViewportRect.height()) / 2));
|
||||||
|
|
||||||
/* XXX Until bug #524925 is fixed, changing the viewport origin will
|
/* XXX Until bug #524925 is fixed, changing the viewport origin will
|
||||||
* probably cause things to be slower than just having a smaller usable
|
* probably cause things to be slower than just having a smaller usable
|
||||||
|
|
|
@ -79,10 +79,6 @@ using mozilla::unused;
|
||||||
|
|
||||||
#include "nsStringGlue.h"
|
#include "nsStringGlue.h"
|
||||||
|
|
||||||
// NB: Keep these in sync with LayerController.java in mobile/android/base and embedding/android/.
|
|
||||||
#define TILE_WIDTH 1024
|
|
||||||
#define TILE_HEIGHT 2048
|
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::widget;
|
using namespace mozilla::widget;
|
||||||
|
|
||||||
|
@ -310,7 +306,7 @@ nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
|
||||||
void
|
void
|
||||||
nsWindow::RedrawAll()
|
nsWindow::RedrawAll()
|
||||||
{
|
{
|
||||||
nsIntRect entireRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
|
nsIntRect entireRect(0, 0, gAndroidBounds.width, gAndroidBounds.height);
|
||||||
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, entireRect);
|
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, entireRect);
|
||||||
nsAppShell::gAppShell->PostEvent(event);
|
nsAppShell::gAppShell->PostEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1014,7 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
||||||
if (coveringChildIndex == -1) {
|
if (coveringChildIndex == -1) {
|
||||||
nsPaintEvent event(true, NS_PAINT, this);
|
nsPaintEvent event(true, NS_PAINT, this);
|
||||||
|
|
||||||
nsIntRect tileRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
|
nsIntRect tileRect(0, 0, gAndroidBounds.width, gAndroidBounds.height);
|
||||||
event.region = boundsRect.Intersect(invalidRect).Intersect(tileRect);
|
event.region = boundsRect.Intersect(invalidRect).Intersect(tileRect);
|
||||||
|
|
||||||
switch (GetLayerManager(nsnull)->GetBackendType()) {
|
switch (GetLayerManager(nsnull)->GetBackendType()) {
|
||||||
|
@ -1112,7 +1108,7 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
||||||
|
|
||||||
unsigned char *bits = client.LockBufferBits();
|
unsigned char *bits = client.LockBufferBits();
|
||||||
nsRefPtr<gfxImageSurface> targetSurface =
|
nsRefPtr<gfxImageSurface> targetSurface =
|
||||||
new gfxImageSurface(bits, gfxIntSize(TILE_WIDTH, TILE_HEIGHT), TILE_WIDTH * 2,
|
new gfxImageSurface(bits, gfxIntSize(gAndroidBounds.width, gAndroidBounds.height), gAndroidBounds.width * 2,
|
||||||
gfxASurface::ImageFormatRGB16_565);
|
gfxASurface::ImageFormatRGB16_565);
|
||||||
if (targetSurface->CairoStatus()) {
|
if (targetSurface->CairoStatus()) {
|
||||||
ALOG("### Failed to create a valid surface from the bitmap");
|
ALOG("### Failed to create a valid surface from the bitmap");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче