bug 746016 - Cache low res version of the page in the java ui for use instead of checkerboarding r=kats

--HG--
extra : rebase_source : d6de0327a46393cd4cfc327dea5689364744a71d
This commit is contained in:
Brad Lassey 2012-04-24 15:13:36 -04:00
parent 4963a1fea8
commit 142a463d8e
20 changed files with 559 additions and 43 deletions

View File

@ -603,9 +603,13 @@ public class GeckoAppShell
imm, text, start, end, newEnd);
}
public static void notifyScreenShot(ByteBuffer data, int tabId, int width, int height) {
// this stub is never called in XUL Fennec, but we need it so that the JNI code
// shared between XUL and Native Fennec doesn't die.
// these 2 stubs are never called in XUL Fennec, but we need them so that
// the JNI code shared between XUL and Native Fennec doesn't die.
public static void notifyScreenShot(final ByteBuffer data, final int tabId, final int x, final int y,
final int width, final int height, final int token) {
}
public static void notifyPaintedRect(float top, float left, float bottom, float right) {
}
private static CountDownLatch sGeckoPendingAcks = null;

View File

@ -728,7 +728,7 @@ pref("direct-texture.force.enabled", false);
pref("direct-texture.force.disabled", false);
// show checkerboard pattern on android; we use background colour instead
pref("gfx.show_checkerboard_pattern", false);
pref("gfx.show_checkerboard_pattern", true);
pref("remote-debugger.enabled", false);
pref("remote-debugger.port", 6000);

View File

@ -556,7 +556,7 @@ abstract public class GeckoApp
int sh = tab.getMinScreenshotHeight();
int dw = tab.getThumbnailWidth();
int dh = tab.getThumbnailHeight();
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), sw, sh, dw, dh));
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, sw, sh, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
}
}
@ -1193,7 +1193,8 @@ abstract public class GeckoApp
tab.updateURL(uri);
tab.setState(Tab.STATE_LOADING);
tab.updateSecurityMode("unknown");
if (Tabs.getInstance().isSelectedTab(tab))
getLayerController().getView().getRenderer().resetCheckerboard();
mMainHandler.post(new Runnable() {
public void run() {
if (Tabs.getInstance().isSelectedTab(tab)) {
@ -1223,6 +1224,11 @@ abstract public class GeckoApp
GeckoAppShell.getHandler().postDelayed(new Runnable() {
public void run() {
getAndProcessThumbnailForTab(tab);
if (Tabs.getInstance().isSelectedTab(tab)) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createStartPaintListentingEvent(tab.getId()));
GeckoAppShell.screenshotWholePage(tab);
}
}
}, 500);
}

View File

@ -39,9 +39,13 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.GeckoLayerClient;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.ScreenshotLayer;
import org.mozilla.gecko.FloatUtils;
import java.io.*;
import java.lang.reflect.*;
@ -110,6 +114,10 @@ public class GeckoAppShell
public static final String SHORTCUT_TYPE_WEBAPP = "webapp";
public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark";
static public final int SCREENSHOT_THUMBNAIL = 0;
static public final int SCREENSHOT_WHOLE_PAGE = 1;
static public final int SCREENSHOT_UPDATE = 2;
static private File sCacheFile = null;
static private int sFreeSpace = -1;
static File sHomeDir = null;
@ -118,6 +126,9 @@ public class GeckoAppShell
private static Boolean sNSSLibsLoaded = false;
private static Boolean sLibsSetup = false;
private static File sGREDir = null;
private static float sCheckerboardPageWidth, sCheckerboardPageHeight;
private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
private static Map<String, CopyOnWriteArrayList<GeckoEventListener>> mEventListeners
= new HashMap<String, CopyOnWriteArrayList<GeckoEventListener>>();
@ -525,8 +536,9 @@ public class GeckoAppShell
mInputConnection.notifyIMEChange(text, start, end, newEnd);
}
public static void notifyScreenShot(final ByteBuffer data, final int tabId,
final int width, final int height) {
// Called by AndroidBridge using JNI
public static void notifyScreenShot(final ByteBuffer data, final int tabId, final int x, final int y,
final int width, final int height, final int token) {
getHandler().post(new Runnable() {
public void run() {
try {
@ -534,9 +546,28 @@ public class GeckoAppShell
if (tab == null)
return;
if (!Tabs.getInstance().isSelectedTab(tab) && SCREENSHOT_THUMBNAIL != token)
return;
Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
b.copyPixelsFromBuffer(data);
GeckoApp.mAppContext.processThumbnail(tab, b, null);
switch (token) {
case SCREENSHOT_WHOLE_PAGE:
GeckoApp.mAppContext.getLayerController()
.getView().getRenderer().setCheckerboardBitmap(b);
break;
case SCREENSHOT_UPDATE:
GeckoApp.mAppContext.getLayerController().getView().getRenderer().
updateCheckerboardBitmap(
b, sLastCheckerboardWidthRatio * x,
sLastCheckerboardHeightRatio * y,
sLastCheckerboardWidthRatio * width,
sLastCheckerboardHeightRatio * height);
break;
case SCREENSHOT_THUMBNAIL:
GeckoApp.mAppContext.processThumbnail(tab, b, null);
break;
}
} finally {
freeDirectBuffer(data);
}
@ -2114,4 +2145,75 @@ public class GeckoAppShell
if (!GeckoApp.mAppContext.showFilePicker(aMimeType, new AsyncResultHandler(id)))
GeckoAppShell.notifyFilePickerResult("", id);
}
static class RepaintRunnable implements Runnable {
private boolean mIsRepaintRunnablePosted = false;
private float mDirtyTop = Float.POSITIVE_INFINITY, mDirtyLeft = Float.POSITIVE_INFINITY;
private float mDirtyBottom = Float.NEGATIVE_INFINITY, mDirtyRight = Float.NEGATIVE_INFINITY;
public void run() {
float top, left, bottom, right;
// synchronize so we don't try to accumulate more rects while painting the ones we have
synchronized(this) {
top = mDirtyTop;
left = mDirtyLeft;
right = mDirtyRight;
bottom = mDirtyBottom;
// reset these to infinity to start accumulating again
mDirtyTop = Float.POSITIVE_INFINITY;
mDirtyLeft = Float.POSITIVE_INFINITY;
mDirtyBottom = Float.NEGATIVE_INFINITY;
mDirtyRight = Float.NEGATIVE_INFINITY;
mIsRepaintRunnablePosted = false;
}
Tab tab = Tabs.getInstance().getSelectedTab();
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
if (FloatUtils.fuzzyEquals(sCheckerboardPageWidth, viewport.pageSizeWidth) &&
FloatUtils.fuzzyEquals(sCheckerboardPageHeight, viewport.pageSizeHeight)) {
float width = right - left;
float height = bottom - top;
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), (int)top, (int)left, (int)width, (int)height, 0, 0, (int)(sLastCheckerboardWidthRatio * width), (int)(sLastCheckerboardHeightRatio * height), GeckoAppShell.SCREENSHOT_UPDATE));
} else {
GeckoAppShell.screenshotWholePage(tab);
}
}
void addRectToRepaint(float top, float left, float bottom, float right) {
synchronized(this) {
mDirtyTop = Math.min(top, mDirtyTop);
mDirtyLeft = Math.min(left, mDirtyLeft);
mDirtyBottom = Math.max(bottom, mDirtyBottom);
mDirtyRight = Math.max(right, mDirtyRight);
if (!mIsRepaintRunnablePosted) {
getHandler().postDelayed(this, 5000);
mIsRepaintRunnablePosted = true;
}
}
}
}
// Called by AndroidBridge using JNI
public static void notifyPaintedRect(float top, float left, float bottom, float right) {
sRepaintRunnable.addRectToRepaint(top, left, bottom, right);
}
public static void screenshotWholePage(Tab tab) {
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
// source width and height to screenshot
float sw = viewport.pageSizeWidth;
float sh = viewport.pageSizeHeight;
// 2Mb of 16bit image data
// may be bumped by up to 4x for power of 2 alignment
float ratio = (float)Math.sqrt((sw * sh) / (ScreenshotLayer.getMaxNumPixels() / 4));
// destination width and hight
int dw = IntSize.nextPowerOfTwo(sw / ratio);
int dh = IntSize.nextPowerOfTwo(sh / ratio);
sLastCheckerboardWidthRatio = dw / sw;
sLastCheckerboardHeightRatio = dh / sh;
sCheckerboardPageWidth = viewport.pageSizeWidth;
sCheckerboardPageHeight = viewport.pageSizeHeight;
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, (int)sw, (int)sh, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
}
}

View File

@ -97,7 +97,8 @@ public class GeckoEvent {
private static final int UNUSED2_EVENT = 26;
private static final int SCREENORIENTATION_CHANGED = 27;
private static final int COMPOSITOR_PAUSE = 28;
private static final int COMPOSITOR_RESUME = 29;
private static final int COMPOSITOR_RESUME = 29;
private static final int PAINT_LISTEN_START_EVENT = 30;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
@ -478,12 +479,15 @@ public class GeckoEvent {
return event;
}
public static GeckoEvent createScreenshotEvent(int tabId, int sw, int sh, int dw, int dh) {
public static GeckoEvent createScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int token) {
GeckoEvent event = new GeckoEvent(SCREENSHOT);
event.mPoints = new Point[2];
event.mPoints[0] = new Point(sw, sh);
event.mPoints[1] = new Point(dw, dh);
event.mPoints = new Point[4];
event.mPoints[0] = new Point(sx, sy);
event.mPoints[1] = new Point(sw, sh);
event.mPoints[2] = new Point(dx, dy);
event.mPoints[3] = new Point(dw, dh);
event.mMetaState = tabId;
event.mFlags = token;
return event;
}
@ -492,4 +496,10 @@ public class GeckoEvent {
event.mScreenOrientation = aScreenOrientation;
return event;
}
public static GeckoEvent createStartPaintListentingEvent(int tabId) {
GeckoEvent event = new GeckoEvent(PAINT_LISTEN_START_EVENT);
event.mMetaState = tabId;
return event;
}
}

View File

@ -138,6 +138,7 @@ FENNEC_JAVA_FILES = \
gfx/PanningPerfAPI.java \
gfx/PointUtils.java \
gfx/RectUtils.java \
gfx/ScreenshotLayer.java \
gfx/ScrollbarLayer.java \
gfx/SingleTileLayer.java \
gfx/SurfaceTextureLayer.java \

View File

@ -43,6 +43,7 @@ import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventResponder;
import org.mozilla.gecko.Tabs;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -341,6 +342,8 @@ public class GeckoLayerClient implements GeckoEventResponder,
mLayerController.abortPanZoomAnimation();
mLayerController.getView().setPaintState(LayerView.PAINT_BEFORE_FIRST);
}
mLayerController.getView().getRenderer().resetCheckerboard();
GeckoAppShell.screenshotWholePage(Tabs.getInstance().getSelectedTab());
}
/** This function is invoked by Gecko via JNI; be careful when modifying signature.

View File

@ -96,6 +96,10 @@ public class IntSize {
return value + 1;
}
public static int nextPowerOfTwo(float value) {
return nextPowerOfTwo((int) value);
}
public IntSize nextPowerOfTwo() {
return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height));
}

View File

@ -52,6 +52,7 @@ import org.mozilla.gecko.gfx.TileLayer;
import org.mozilla.gecko.GeckoAppShell;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@ -90,8 +91,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
private final LayerView mView;
private final SingleTileLayer mBackgroundLayer;
private final CheckerboardImage mCheckerboardImage;
private final SingleTileLayer mCheckerboardLayer;
private final ScreenshotLayer mCheckerboardLayer;
private final NinePatchTileLayer mShadowLayer;
private TextLayer mFrameRateLayer;
private final ScrollbarLayer mHorizScrollLayer;
@ -158,6 +158,36 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
" gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" +
"}\n";
public void setCheckerboardBitmap(Bitmap bitmap) {
mCheckerboardLayer.setBitmap(bitmap);
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
}
}
public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y, float width, float height) {
mCheckerboardLayer.updateBitmap(bitmap, x, y, width, height);
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
}
}
public void resetCheckerboard() {
mCheckerboardLayer.reset();
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
}
}
public LayerRenderer(LayerView view) {
mView = view;
@ -166,8 +196,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern());
mBackgroundLayer = new SingleTileLayer(true, backgroundImage);
mCheckerboardImage = new CheckerboardImage();
mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage);
mCheckerboardLayer = ScreenshotLayer.create();
CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern());
mShadowLayer = new NinePatchTileLayer(shadowImage);
@ -391,18 +420,15 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
private void updateCheckerboardImage() {
int checkerboardColor = mView.getController().getCheckerboardColor();
boolean showChecks = mView.getController().checkerboardShouldShowChecks();
if (checkerboardColor == mCheckerboardImage.getColor() &&
showChecks == mCheckerboardImage.getShowChecks()) {
return;
}
mCheckerboardLayer.beginTransaction(); // called on compositor thread
try {
mCheckerboardImage.update(showChecks, checkerboardColor);
mCheckerboardLayer.invalidate();
if (mCheckerboardLayer.updateBackground(showChecks, checkerboardColor))
mCheckerboardLayer.invalidate();
} finally {
mCheckerboardLayer.endTransaction();
}
}
/*
@ -533,7 +559,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mUpdated &= mBackgroundLayer.update(mScreenContext); // called on compositor thread
mUpdated &= mShadowLayer.update(mPageContext); // called on compositor thread
updateCheckerboardImage();
mUpdated &= mCheckerboardLayer.update(mScreenContext); // called on compositor thread
mUpdated &= mCheckerboardLayer.update(mPageContext); // called on compositor thread
if (mFrameRateLayer != null) mUpdated &= mFrameRateLayer.update(mScreenContext); // called on compositor thread
mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread
mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
@ -569,7 +595,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
/* Draw the checkerboard. */
setScissorRect();
mCheckerboardLayer.setMask(rootMask);
mCheckerboardLayer.draw(mScreenContext);
mCheckerboardLayer.draw(mPageContext);
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
}

View File

@ -230,7 +230,7 @@ public class LayerView extends SurfaceView implements SurfaceHolder.Callback {
}
public GLSurfaceView.Renderer getRenderer() {
public LayerRenderer getRenderer() {
return mRenderer;
}

View File

@ -57,7 +57,7 @@ public class NinePatchTileLayer extends TileLayer {
private static final int TEXTURE_SIZE = 64;
public NinePatchTileLayer(CairoImage image) {
super(false, image);
super(image, TileLayer.PaintMode.NORMAL);
}
@Override

View File

@ -0,0 +1,254 @@
/* -*- 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 org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.SingleTileLayer;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
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.ByteBuffer;
import java.nio.IntBuffer;
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 = GeckoAppShell.allocateDirectBuffer(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)
GeckoAppShell.freeDirectBuffer(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 IntSize getSize() { return mSize; }
@Override
public int getFormat() { return mFormat; }
}
}

View File

@ -141,7 +141,7 @@ public class ScrollbarLayer extends TileLayer {
};
private ScrollbarLayer(LayerRenderer renderer, CairoImage image, boolean vertical, ByteBuffer buffer) {
super(false, image);
super(image, TileLayer.PaintMode.NORMAL);
mVertical = vertical;
mBuffer = buffer;
mRenderer = renderer;

View File

@ -65,7 +65,11 @@ public class SingleTileLayer extends TileLayer {
public SingleTileLayer(CairoImage image) { this(false, image); }
public SingleTileLayer(boolean repeat, CairoImage image) {
super(repeat, image);
super(image, repeat ? TileLayer.PaintMode.REPEAT : TileLayer.PaintMode.NORMAL);
}
public SingleTileLayer(CairoImage image, TileLayer.PaintMode paintMode) {
super(image, paintMode);
}
/**
@ -86,7 +90,7 @@ public class SingleTileLayer extends TileLayer {
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());

View File

@ -58,20 +58,23 @@ public abstract class TileLayer extends Layer {
private final Rect mDirtyRect;
private final CairoImage mImage;
private final boolean mRepeat;
private IntSize mSize;
private int[] mTextureIDs;
public TileLayer(boolean repeat, CairoImage image) {
public enum PaintMode { NORMAL, REPEAT, STRETCH };
private PaintMode mPaintMode;
public TileLayer(CairoImage image, PaintMode paintMode) {
super(image.getSize());
mRepeat = repeat;
mPaintMode = paintMode;
mImage = image;
mSize = new IntSize(0, 0);
mDirtyRect = new Rect();
}
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; }
@ -186,7 +189,7 @@ public abstract class TileLayer extends Layer {
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
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);
}

View File

@ -115,7 +115,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Z)V");
jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V");
jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;III)V");
jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;IIIIII)V");
jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V");
@ -166,6 +166,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V");
jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[D");
jRemovePluginView = jEnv->GetStaticMethodID(jGeckoAppShellClass, "removePluginView", "(Landroid/view/View;)V");
jNotifyPaintedRect = jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyPaintedRect", "(FFFF)V");
jHandleGeckoMessage = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
jCheckUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "checkUriVisited", "(Ljava/lang/String;)V");
@ -2135,7 +2136,7 @@ jobject JNICALL
Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *jenv, jclass, jlong size);
nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale)
nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token)
{
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
if (!win)
@ -2175,6 +2176,15 @@ nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt
nsresult rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
NS_ENSURE_SUCCESS(rv, rv);
AndroidBridge::AutoLocalJNIFrame jniFrame(jenv, 1);
jenv->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, dstW, dstH);
jenv->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, srcX * dstW / srcW , srcY * dstH / srcH, dstW, dstH, token);
return NS_OK;
}
void
AndroidBridge::NotifyPaintedRect(float top, float left, float bottom, float right)
{
JNIEnv* jenv = AndroidBridge::GetJNIEnv();
if (!jenv)
return;
jenv->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyPaintedRect, top, left, bottom, right);
}

View File

@ -183,7 +183,9 @@ public:
static void RemovePluginView(void* surface);
nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale);
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);
void AcknowledgeEventSync();
@ -524,6 +526,7 @@ protected:
jmethodID jCheckUriVisited;
jmethodID jMarkUriVisited;
jmethodID jRemovePluginView;
jmethodID jNotifyPaintedRect;
jmethodID jNumberOfMessages;
jmethodID jSendMessage;

View File

@ -520,7 +520,13 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
case SCREENSHOT: {
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
ReadPointArray(mPoints, jenv, jPoints, 2);
mFlags = jenv->GetIntField(jobj, jFlagsField);
ReadPointArray(mPoints, jenv, jPoints, 4);
break;
}
case PAINT_LISTEN_START_EVENT: {
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
break;
}

View File

@ -719,6 +719,7 @@ public:
SCREENORIENTATION_CHANGED = 27,
COMPOSITOR_PAUSE = 28,
COMPOSITOR_RESUME = 29,
PAINT_LISTEN_START_EVENT = 30,
dummy_java_enum_list_end
};

View File

@ -49,6 +49,10 @@
#include "nsIAppStartup.h"
#include "nsIGeolocationProvider.h"
#include "nsCacheService.h"
#include "nsIDOMEventListener.h"
#include "nsDOMNotifyPaintEvent.h"
#include "nsIDOMClientRectList.h"
#include "nsIDOMClientRect.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
@ -92,6 +96,64 @@ nsAppShell *nsAppShell::gAppShell = nsnull;
NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
class AfterPaintListener : public nsIDOMEventListener {
public:
NS_DECL_ISUPPORTS
void Register(nsIDOMWindow* window) {
if (mEventTarget)
Unregister();
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
if (!win)
return;
mEventTarget = win->GetChromeEventHandler();
if (mEventTarget)
mEventTarget->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), this, false);
}
void Unregister() {
if (mEventTarget)
mEventTarget->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), this, false);
mEventTarget = nsnull;
}
virtual nsresult HandleEvent(nsIDOMEvent* aEvent) {
nsCOMPtr<nsIDOMNotifyPaintEvent> paintEvent = do_QueryInterface(aEvent);
if (!paintEvent)
return NS_OK;
nsCOMPtr<nsIDOMClientRectList> rects;
paintEvent->GetClientRects(getter_AddRefs(rects));
if (!rects)
return NS_OK;
PRUint32 length;
rects->GetLength(&length);
for (PRUint32 i = 0; i < length; ++i) {
float top, left, bottom, right;
nsCOMPtr<nsIDOMClientRect> rect = rects->GetItemAt(i);
if (!rect)
continue;
rect->GetTop(&top);
rect->GetLeft(&left);
rect->GetRight(&right);
rect->GetBottom(&bottom);
AndroidBridge::NotifyPaintedRect(top, left, bottom, right);
}
return NS_OK;
}
~AfterPaintListener() {
if (mEventTarget)
Unregister();
}
private:
nsCOMPtr<nsIDOMEventTarget> mEventTarget;
};
NS_IMPL_ISUPPORTS1(AfterPaintListener, nsIDOMEventListener)
nsCOMPtr<AfterPaintListener> sAfterPaintListener = nsnull;
nsAppShell::nsAppShell()
: mQueueLock("nsAppShell.mQueueLock"),
mCondLock("nsAppShell.mCondLock"),
@ -101,11 +163,13 @@ nsAppShell::nsAppShell()
mAllowCoalescingNextDraw(false)
{
gAppShell = this;
sAfterPaintListener = new AfterPaintListener();
}
nsAppShell::~nsAppShell()
{
gAppShell = nsnull;
delete sAfterPaintListener;
}
void
@ -369,6 +433,21 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
break;
}
case AndroidGeckoEvent::PAINT_LISTEN_START_EVENT: {
nsCOMPtr<nsIDOMWindow> domWindow;
nsCOMPtr<nsIBrowserTab> tab;
mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab));
if (!tab)
break;
tab->GetWindow(getter_AddRefs(domWindow));
if (!domWindow)
break;
sAfterPaintListener->Register(domWindow);
break;
}
case AndroidGeckoEvent::SCREENSHOT: {
if (!mBrowserApp)
break;
@ -392,8 +471,8 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
break;
nsTArray<nsIntPoint> points = curEvent->Points();
NS_ASSERTION(points.Length() == 2, "Screenshot event does not have enough coordinates");
bridge->TakeScreenshot(domWindow, 0, 0, points[0].x, points[0].y, points[1].x, points[1].y, curEvent->MetaState(), scale);
NS_ASSERTION(points.Length() == 4, "Screenshot event does not have enough coordinates");
bridge->TakeScreenshot(domWindow, points[0].x, points[0].y, points[1].x, points[1].y, points[3].x, points[3].y, curEvent->MetaState(), scale, curEvent->Flags());
break;
}