Bug 748718 - Make ScreenshotLayer use SingleTileLayer's draw function. r=kats

ScreenshotLayer overrides SingleTileLayer's draw function to simplify the
drawing process. Unfortunately, this doesn't respect the layer mask, and can
cause the layer to appear incorrectly stretched.

Change the screenshot process so that whole-page screenshots pass parameters
that are pre-scaled (to avoid mismatches when the viewport changes), and set
the page size on the ScreenShotLayer so it draws in the correct place.

This also alters the masking slightly to avoid visible seams around masks in
the centre of the page, and to avoid unnnecessary drawing at the edges of the
page.

This change fixes the frame-rate issues with the screenshot layer, as it
removes unnecessary over-draw.

--HG--
extra : rebase_source : 671c5a48d7bc08b3b452ed7eea228eb22843c1ce
This commit is contained in:
Chris Lord 2012-04-26 13:45:31 -04:00
parent 27218c111a
commit 6e7a025ac6
7 changed files with 89 additions and 92 deletions

View File

@ -558,15 +558,19 @@ public class GeckoAppShell
switch (token) {
case SCREENSHOT_WHOLE_PAGE:
GeckoApp.mAppContext.getLayerController()
.getView().getRenderer().setCheckerboardBitmap(b);
.getView().getRenderer()
.setCheckerboardBitmap(b, sCheckerboardPageWidth,
sCheckerboardPageHeight);
break;
case SCREENSHOT_UPDATE:
GeckoApp.mAppContext.getLayerController().getView().getRenderer().
updateCheckerboardBitmap(
b, sLastCheckerboardWidthRatio * x,
b, sLastCheckerboardWidthRatio * x,
sLastCheckerboardHeightRatio * y,
sLastCheckerboardWidthRatio * width,
sLastCheckerboardHeightRatio * height);
sLastCheckerboardWidthRatio * width,
sLastCheckerboardHeightRatio * height,
sCheckerboardPageWidth,
sCheckerboardPageHeight);
break;
case SCREENSHOT_THUMBNAIL:
GeckoApp.mAppContext.processThumbnail(tab, b, null);
@ -2217,9 +2221,10 @@ public class GeckoAppShell
return;
}
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
Log.i(LOGTAG, "Taking whole-screen screenshot, viewport: " + viewport);
// source width and height to screenshot
float sw = viewport.pageSizeWidth;
float sh = viewport.pageSizeHeight;
float sw = viewport.pageSizeWidth / viewport.zoomFactor;
float sh = viewport.pageSizeHeight / viewport.zoomFactor;
int maxPixels = Math.min(ScreenshotLayer.getMaxNumPixels(), sMaxTextureSize * sMaxTextureSize);
// 2Mb of 16bit image data
// may be bumped by up to 4x for power of 2 alignment
@ -2234,8 +2239,8 @@ public class GeckoAppShell
sLastCheckerboardWidthRatio = dw / sw;
sLastCheckerboardHeightRatio = dh / sh;
sCheckerboardPageWidth = viewport.pageSizeWidth;
sCheckerboardPageHeight = viewport.pageSizeHeight;
sCheckerboardPageWidth = sw;
sCheckerboardPageHeight = sh;
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, (int)sw, (int)sh, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
}

View File

@ -158,20 +158,26 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
" gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" +
"}\n";
public void setCheckerboardBitmap(Bitmap bitmap) {
public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) {
mCheckerboardLayer.setBitmap(bitmap);
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth),
Math.round(pageHeight)));
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
}
}
public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y, float width, float height) {
public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y,
float width, float height,
float pageWidth, float pageHeight) {
mCheckerboardLayer.updateBitmap(bitmap, x, y, width, height);
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.setPosition(new Rect(0, 0, Math.round(pageWidth),
Math.round(pageHeight)));
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
@ -570,6 +576,39 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
}
/** Retrieves the bounds for the layer, rounded in such a way that it
* can be used as a mask for something that will render underneath it.
* This will round the bounds inwards, but stretch the mask towards any
* near page edge, where near is considered to be 'within 2 pixels'.
* Returns null if the given layer is null.
*/
private Rect getMaskForLayer(Layer layer) {
if (layer == null) {
return null;
}
RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f);
Rect mask = RectUtils.roundIn(bounds);
// If the mask is within two pixels of any page edge, stretch it over
// that edge. This is to avoid drawing thin slivers when masking
// layers.
if (mask.top <= 2) {
mask.top = -1;
}
if (mask.left <= 2) {
mask.left = -1;
}
if (mask.right >= mPageRect.right - 2) {
mask.right = mPageRect.right + 1;
}
if (mask.bottom >= mPageRect.bottom - 2) {
mask.bottom = mPageRect.bottom + 1;
}
return mask;
}
/** This function is invoked via JNI; be careful when modifying signature. */
public void drawBackground() {
/* Draw the background. */
@ -582,15 +621,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
if (!untransformedPageRect.contains(mView.getController().getViewport()))
mShadowLayer.draw(mPageContext);
/* Find the area the root layer will render into, to mask the scissor rect */
Rect rootMask = null;
Layer rootLayer = mView.getController().getRoot();
if (rootLayer != null) {
RectF rootBounds = rootLayer.getBounds(mPageContext);
rootBounds.offset(-mPageContext.viewport.left, -mPageContext.viewport.top);
rootMask = new Rect();
rootBounds.roundOut(rootMask);
}
/* Find the area the root layer will render into, to mask the checkerboard layer */
Rect rootMask = getMaskForLayer(mView.getController().getRoot());
/* Draw the checkerboard. */
setScissorRect();

View File

@ -77,6 +77,15 @@ public final class RectUtils {
rect.bottom + halfMoreHeight);
}
public static RectF contract(RectF rect, float lessWidth, float lessHeight) {
float halfLessWidth = lessWidth / 2.0f;
float halfLessHeight = lessHeight / 2.0f;
return new RectF(rect.left + halfLessWidth,
rect.top + halfLessHeight,
rect.right - halfLessWidth,
rect.bottom - halfLessHeight);
}
public static RectF intersect(RectF one, RectF two) {
float left = Math.max(one.left, two.left);
float top = Math.max(one.top, two.top);
@ -99,6 +108,11 @@ public final class RectUtils {
Math.round(rect.right), Math.round(rect.bottom));
}
public static Rect roundIn(RectF rect) {
return new Rect((int)Math.ceil(rect.left), (int)Math.ceil(rect.top),
(int)Math.floor(rect.right), (int)Math.floor(rect.bottom));
}
public static IntSize getSize(Rect rect) {
return new IntSize(rect.width(), rect.height());
}

View File

@ -8,6 +8,7 @@ package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.SingleTileLayer;
import android.graphics.Bitmap;
@ -47,6 +48,7 @@ public class ScreenshotLayer extends SingleTileLayer {
public void reset() {
mIsSingleColor = true;
updateBackground(mForceSingleColor, Color.WHITE);
setPaintMode(TileLayer.PaintMode.STRETCH);
}
void setBitmap(Bitmap bitmap) {
@ -58,6 +60,7 @@ public class ScreenshotLayer extends SingleTileLayer {
mBufferSize = new IntSize(width, height);
mImage.setBitmap(bitmap, width, height, CairoImage.FORMAT_RGB16_565);
mIsSingleColor = false;
setPaintMode(TileLayer.PaintMode.NORMAL);
}
public void updateBitmap(Bitmap bitmap, float x, float y, float width, float height) {
@ -94,70 +97,6 @@ public class ScreenshotLayer extends SingleTileLayer {
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;

View File

@ -84,12 +84,8 @@ public abstract class TileLayer extends Layer {
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!");
}
super.setPosition(newPosition);
public void setPaintMode(PaintMode mode) {
mPaintMode = mode;
}
/**

View File

@ -183,6 +183,13 @@ public:
static void RemovePluginView(void* surface);
/* These are defined in mobile/android/base/GeckoAppShell.java */
enum {
SCREENSHOT_THUMBNAIL = 0,
SCREENSHOT_WHOLE_PAGE = 1,
SCREENSHOT_UPDATE = 2
};
nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token);
static void NotifyPaintedRect(float top, float left, float bottom, float right);

View File

@ -456,9 +456,10 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
if (!bridge)
break;
PRInt32 token = curEvent->Flags();
nsCOMPtr<nsIDOMWindow> domWindow;
nsCOMPtr<nsIBrowserTab> tab;
float scale;
mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab));
if (!tab)
break;
@ -467,8 +468,11 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
if (!domWindow)
break;
if (NS_FAILED(tab->GetScale(&scale)))
break;
float scale = 1.0;
if (token == AndroidBridge::SCREENSHOT_THUMBNAIL) {
if (NS_FAILED(tab->GetScale(&scale)))
break;
}
nsTArray<nsIntPoint> points = curEvent->Points();
NS_ASSERTION(points.Length() == 4, "Screenshot event does not have enough coordinates");