зеркало из https://github.com/mozilla/gecko-dev.git
bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, chunk up large updates as well r=kats
--HG-- extra : rebase_source : a5e6c489fad35479a833216c30ac8e8a3ff17058
This commit is contained in:
Коммит
5300717c62
|
@ -256,6 +256,14 @@ void MessageLoop::PostNonNestableDelayedTask(
|
|||
PostTask_Helper(from_here, task, delay_ms, false);
|
||||
}
|
||||
|
||||
void MessageLoop::PostIdleTask(
|
||||
const tracked_objects::Location& from_here, Task* task) {
|
||||
DCHECK(current() == this);
|
||||
task->SetBirthPlace(from_here);
|
||||
PendingTask pending_task(task, false);
|
||||
deferred_non_nestable_work_queue_.push(pending_task);
|
||||
}
|
||||
|
||||
// Possibly called on a background thread!
|
||||
void MessageLoop::PostTask_Helper(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms,
|
||||
|
|
|
@ -121,6 +121,10 @@ public:
|
|||
void PostNonNestableDelayedTask(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms);
|
||||
|
||||
// PostIdleTask is not thread safe and should be called on this thread
|
||||
void PostIdleTask(
|
||||
const tracked_objects::Location& from_here, Task* task);
|
||||
|
||||
// A variant on PostTask that deletes the given object. This is useful
|
||||
// if the object needs to live until the next run of the MessageLoop (for
|
||||
// example, deleting a RenderProcessHost from within an IPC callback is not
|
||||
|
|
|
@ -688,7 +688,7 @@ abstract public class GeckoApp
|
|||
|
||||
int dw = tab.getThumbnailWidth();
|
||||
int dh = tab.getThumbnailHeight();
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL, tab.getThumbnailBuffer()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.mozilla.gecko.gfx.ScreenshotLayer;
|
|||
import org.mozilla.gecko.FloatUtils;
|
||||
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
|
||||
import org.mozilla.gecko.gfx.ViewportMetrics;
|
||||
import org.mozilla.gecko.gfx.RectUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
|
@ -83,8 +84,7 @@ public class GeckoAppShell
|
|||
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 public final int SCREENSHOT_CHECKERBOARD = 1;
|
||||
|
||||
static public final int RESTORE_NONE = 0;
|
||||
static public final int RESTORE_OOM = 1;
|
||||
|
@ -98,11 +98,6 @@ public class GeckoAppShell
|
|||
private static Boolean sNSSLibsLoaded = false;
|
||||
private static Boolean sLibsSetup = false;
|
||||
private static File sGREDir = null;
|
||||
private static RectF sCheckerboardPageRect;
|
||||
private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
|
||||
private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
|
||||
static private int sMaxTextureSize = 0;
|
||||
|
||||
private static Map<String, CopyOnWriteArrayList<GeckoEventListener>> mEventListeners
|
||||
= new HashMap<String, CopyOnWriteArrayList<GeckoEventListener>>();
|
||||
|
||||
|
@ -128,8 +123,6 @@ public class GeckoAppShell
|
|||
|
||||
private static Handler sGeckoHandler;
|
||||
|
||||
private static boolean sDisableScreenshot = false;
|
||||
|
||||
/* The Android-side API: API methods that Android calls */
|
||||
|
||||
// Initialization methods
|
||||
|
@ -535,42 +528,7 @@ public class GeckoAppShell
|
|||
// 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 {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
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);
|
||||
switch (token) {
|
||||
case SCREENSHOT_WHOLE_PAGE:
|
||||
GeckoApp.mAppContext.getLayerController()
|
||||
.getView().getRenderer()
|
||||
.setCheckerboardBitmap(b, sCheckerboardPageRect);
|
||||
break;
|
||||
case SCREENSHOT_UPDATE:
|
||||
GeckoApp.mAppContext.getLayerController().getView().getRenderer().
|
||||
updateCheckerboardBitmap(
|
||||
b, sLastCheckerboardWidthRatio * x,
|
||||
sLastCheckerboardHeightRatio * y,
|
||||
sLastCheckerboardWidthRatio * width,
|
||||
sLastCheckerboardHeightRatio * height,
|
||||
sCheckerboardPageRect);
|
||||
break;
|
||||
case SCREENSHOT_THUMBNAIL:
|
||||
GeckoApp.mAppContext.processThumbnail(tab, b, null);
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
freeDirectBuffer(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
ScreenshotHandler.notifyScreenShot(data, tabId, x, y, width, height, token);
|
||||
}
|
||||
|
||||
private static CountDownLatch sGeckoPendingAcks = null;
|
||||
|
@ -2136,6 +2094,26 @@ public class GeckoAppShell
|
|||
if (!GeckoApp.mAppContext.showFilePicker(aMimeType, new AsyncResultHandler(id)))
|
||||
GeckoAppShell.notifyFilePickerResult("", id);
|
||||
}
|
||||
public static void screenshotWholePage(Tab tab) {
|
||||
ScreenshotHandler.screenshotWholePage(tab);
|
||||
}
|
||||
|
||||
// Called by AndroidBridge using JNI
|
||||
public static void notifyPaintedRect(float top, float left, float bottom, float right) {
|
||||
ScreenshotHandler.notifyPaintedRect(top, left, bottom, right);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotHandler {
|
||||
private static Queue<PendingScreenshot> sPendingScreenshots = new ArrayDeque<PendingScreenshot>();
|
||||
private static RectF sCheckerboardPageRect;
|
||||
private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
|
||||
private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
|
||||
private static int sMaxTextureSize = 0;
|
||||
private static final String LOGTAG = "GeckoScreenshot";
|
||||
private static boolean sDisableScreenshot = false;
|
||||
private static ByteBuffer sWholePageScreenshotBuffer;
|
||||
private static int sCheckerboardBufferWidth, sCheckerboardBufferHeight;
|
||||
|
||||
static class RepaintRunnable implements Runnable {
|
||||
private boolean mIsRepaintRunnablePosted = false;
|
||||
|
@ -2158,25 +2136,40 @@ public class GeckoAppShell
|
|||
mIsRepaintRunnablePosted = false;
|
||||
}
|
||||
|
||||
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
GeckoAppShell.screenshotWholePage(tab);
|
||||
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
|
||||
|
||||
if (RectUtils.fuzzyEquals(sCheckerboardPageRect, viewport.getCssPageRect())) {
|
||||
float width = right - left;
|
||||
float height = bottom - top;
|
||||
scheduleCheckerboardScreenshotEvent(tab.getId(),
|
||||
(int)left, (int)top, (int)width, (int)height,
|
||||
(int)(sLastCheckerboardWidthRatio * left),
|
||||
(int)(sLastCheckerboardHeightRatio * top),
|
||||
(int)(sLastCheckerboardWidthRatio * width),
|
||||
(int)(sLastCheckerboardHeightRatio * height),
|
||||
sCheckerboardBufferWidth, sCheckerboardBufferHeight);
|
||||
} 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);
|
||||
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
|
||||
mDirtyTop = Math.max(sCheckerboardPageRect.top, Math.min(top, mDirtyTop));
|
||||
mDirtyLeft = Math.max(sCheckerboardPageRect.left, Math.min(left, mDirtyLeft));
|
||||
mDirtyBottom = Math.min(sCheckerboardPageRect.bottom, Math.max(bottom, mDirtyBottom));
|
||||
mDirtyRight = Math.min(sCheckerboardPageRect.right, Math.max(right, mDirtyRight));
|
||||
if (!mIsRepaintRunnablePosted) {
|
||||
getHandler().postDelayed(this, 5000);
|
||||
GeckoAppShell.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);
|
||||
}
|
||||
|
@ -2203,7 +2196,9 @@ public class GeckoAppShell
|
|||
sMaxTextureSize = maxTextureSize[0];
|
||||
if (sMaxTextureSize == 0)
|
||||
return;
|
||||
sWholePageScreenshotBuffer = GeckoAppShell.allocateDirectBuffer(ScreenshotLayer.getMaxNumPixels() * 2 /* 16 bpp */);
|
||||
}
|
||||
|
||||
ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
|
||||
Log.i(LOGTAG, "Taking whole-screen screenshot, viewport: " + viewport);
|
||||
// source width and height to screenshot
|
||||
|
@ -2224,14 +2219,96 @@ public class GeckoAppShell
|
|||
int dy = 0;
|
||||
int dw = clamp(minTextureSize, idealDstWidth, sMaxTextureSize);
|
||||
int dh = maxPixels / dw;
|
||||
sCheckerboardBufferWidth = dw;
|
||||
sCheckerboardBufferHeight = dh;
|
||||
|
||||
sLastCheckerboardWidthRatio = dw / sw;
|
||||
sLastCheckerboardHeightRatio = dh / sh;
|
||||
sCheckerboardPageRect = viewport.getCssPageRect();
|
||||
scheduleCheckerboardScreenshotEvent(tab.getId(), (int)sx, (int)sy, (int)sw, (int)sh, dx, dy, dw, dh, dw, dh);
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(),
|
||||
(int)FloatMath.ceil(sx), (int)FloatMath.ceil(sy),
|
||||
(int)FloatMath.floor(sw), (int)FloatMath.floor(sh),
|
||||
dx, dy, dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
|
||||
static void scheduleCheckerboardScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh) {
|
||||
float totalSize = sw * sh;
|
||||
int numSlices = (int) Math.ceil(totalSize / 100000);
|
||||
int srcSliceSize = (int) Math.ceil(sh / numSlices);
|
||||
int dstSliceSize = (int) Math.ceil(dh / numSlices);
|
||||
for (int i = 0; i < numSlices; i++) {
|
||||
GeckoEvent event =
|
||||
GeckoEvent.createScreenshotEvent(tabId,
|
||||
sx, sy + srcSliceSize * i, sw, srcSliceSize,
|
||||
dx, dy + dstSliceSize * i, dw, dstSliceSize, bw, bh,
|
||||
GeckoAppShell.SCREENSHOT_CHECKERBOARD,
|
||||
sWholePageScreenshotBuffer);
|
||||
synchronized(sPendingScreenshots) {
|
||||
sPendingScreenshots.add(new PendingScreenshot(tabId, event));
|
||||
if (sPendingScreenshots.size() == 1)
|
||||
sendNextEventToGecko();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sendNextEventToGecko() {
|
||||
synchronized(sPendingScreenshots) {
|
||||
if (sPendingScreenshots.isEmpty())
|
||||
return;
|
||||
GeckoAppShell.sendEventToGecko(sPendingScreenshots.element().getEvent());
|
||||
}
|
||||
}
|
||||
|
||||
static class PendingScreenshot {
|
||||
private final GeckoEvent mEvent;
|
||||
private final int mTabId;
|
||||
|
||||
PendingScreenshot(int tabId, GeckoEvent event) {
|
||||
mTabId = tabId;
|
||||
mEvent = event;
|
||||
}
|
||||
|
||||
GeckoEvent getEvent() {
|
||||
return mEvent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null) {
|
||||
if (token == GeckoAppShell.SCREENSHOT_CHECKERBOARD) {
|
||||
synchronized(sPendingScreenshots) {
|
||||
sPendingScreenshots.remove();
|
||||
sendNextEventToGecko();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (token) {
|
||||
case GeckoAppShell.SCREENSHOT_CHECKERBOARD:
|
||||
{
|
||||
GeckoApp.mAppContext.getLayerController()
|
||||
.getView().getRenderer()
|
||||
.setCheckerboardBitmap(data, width, height, sCheckerboardPageRect);
|
||||
synchronized(sPendingScreenshots) {
|
||||
sPendingScreenshots.remove();
|
||||
sendNextEventToGecko();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GeckoAppShell.SCREENSHOT_THUMBNAIL:
|
||||
{
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
Bitmap b = tab.getThumbnailBitmap();
|
||||
b.copyPixelsFromBuffer(data);
|
||||
GeckoApp.mAppContext.processThumbnail(tab, b, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.text.format.Time;
|
|||
import android.os.SystemClock;
|
||||
import java.lang.Math;
|
||||
import java.lang.System;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -116,6 +117,8 @@ public class GeckoEvent {
|
|||
|
||||
public short mScreenOrientation;
|
||||
|
||||
public ByteBuffer mBuffer;
|
||||
|
||||
private GeckoEvent(int evType) {
|
||||
mType = evType;
|
||||
}
|
||||
|
@ -456,15 +459,17 @@ public class GeckoEvent {
|
|||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int token) {
|
||||
public static GeckoEvent createScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh, int token, ByteBuffer buffer) {
|
||||
GeckoEvent event = new GeckoEvent(SCREENSHOT);
|
||||
event.mPoints = new Point[4];
|
||||
event.mPoints = new Point[5];
|
||||
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.mPoints[4] = new Point(bw, bh);
|
||||
event.mMetaState = tabId;
|
||||
event.mFlags = token;
|
||||
event.mBuffer = buffer;
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.json.JSONObject;
|
|||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.gfx.Layer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
@ -64,6 +65,8 @@ public final class Tab {
|
|||
private ContentObserver mContentObserver;
|
||||
private int mCheckerboardColor = Color.WHITE;
|
||||
private int mState;
|
||||
private ByteBuffer mThumbnailBuffer;
|
||||
private Bitmap mThumbnailBitmap;
|
||||
|
||||
public static final int STATE_DELAYED = 0;
|
||||
public static final int STATE_LOADING = 1;
|
||||
|
@ -138,6 +141,31 @@ public final class Tab {
|
|||
return mThumbnail;
|
||||
}
|
||||
|
||||
synchronized public ByteBuffer getThumbnailBuffer() {
|
||||
int capacity = getThumbnailWidth() * getThumbnailHeight() * 2 /* 16 bpp */;
|
||||
if (mThumbnailBuffer != null && mThumbnailBuffer.capacity() == capacity)
|
||||
return mThumbnailBuffer;
|
||||
if (mThumbnailBuffer != null)
|
||||
GeckoAppShell.freeDirectBuffer(mThumbnailBuffer); // not calling freeBuffer() because it would deadlock
|
||||
return mThumbnailBuffer = GeckoAppShell.allocateDirectBuffer(capacity);
|
||||
}
|
||||
|
||||
public Bitmap getThumbnailBitmap() {
|
||||
if (mThumbnailBitmap != null)
|
||||
return mThumbnailBitmap;
|
||||
return mThumbnailBitmap = Bitmap.createBitmap(getThumbnailWidth(), getThumbnailHeight(), Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
freeBuffer();
|
||||
}
|
||||
|
||||
synchronized void freeBuffer() {
|
||||
if (mThumbnailBuffer != null)
|
||||
GeckoAppShell.freeDirectBuffer(mThumbnailBuffer);
|
||||
mThumbnailBuffer = null;
|
||||
}
|
||||
|
||||
float getDensity() {
|
||||
if (sDensity == 0.0f) {
|
||||
sDensity = GeckoApp.mAppContext.getDisplayMetrics().density;
|
||||
|
@ -159,13 +187,10 @@ public final class Tab {
|
|||
public void run() {
|
||||
if (b != null) {
|
||||
try {
|
||||
Bitmap bitmap = Bitmap.createScaledBitmap(b, getThumbnailWidth(), getThumbnailHeight(), false);
|
||||
|
||||
if (mState == Tab.STATE_SUCCESS)
|
||||
saveThumbnailToDB(new BitmapDrawable(bitmap));
|
||||
saveThumbnailToDB(new BitmapDrawable(b));
|
||||
|
||||
mThumbnail = new BitmapDrawable(bitmap);
|
||||
b.recycle();
|
||||
mThumbnail = new BitmapDrawable(b);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.e(LOGTAG, "Unable to create/scale bitmap", oom);
|
||||
mThumbnail = null;
|
||||
|
|
|
@ -76,8 +76,10 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
public void removeTab(int id) {
|
||||
if (tabs.containsKey(id)) {
|
||||
order.remove(getTab(id));
|
||||
Tab tab = getTab(id);
|
||||
order.remove(tab);
|
||||
tabs.remove(id);
|
||||
tab.freeBuffer();
|
||||
Log.i(LOGTAG, "Removed a tab with id: " + id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,5 +71,11 @@ public class IntSize {
|
|||
public IntSize nextPowerOfTwo() {
|
||||
return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height));
|
||||
}
|
||||
|
||||
public static boolean isPowerOfTwo(int value) {
|
||||
if (value == 0)
|
||||
return false;
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,8 +134,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
" gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
|
||||
"}\n";
|
||||
|
||||
public void setCheckerboardBitmap(Bitmap bitmap, RectF pageRect) {
|
||||
mCheckerboardLayer.setBitmap(bitmap);
|
||||
public void setCheckerboardBitmap(ByteBuffer data, int width, int height, RectF pageRect) {
|
||||
mCheckerboardLayer.setBitmap(data, width, height);
|
||||
mCheckerboardLayer.beginTransaction();
|
||||
try {
|
||||
mCheckerboardLayer.setPosition(RectUtils.round(pageRect));
|
||||
|
|
|
@ -35,6 +35,7 @@ public class ScreenshotLayer extends SingleTileLayer {
|
|||
private IntSize mImageSize;
|
||||
// Whether we have an up-to-date image to draw
|
||||
private boolean mHasImage;
|
||||
private static String LOGTAG = "GeckoScreenshot";
|
||||
|
||||
public static int getMaxNumPixels() {
|
||||
return SCREENSHOT_SIZE_LIMIT;
|
||||
|
@ -44,6 +45,19 @@ public class ScreenshotLayer extends SingleTileLayer {
|
|||
mHasImage = false;
|
||||
}
|
||||
|
||||
void setBitmap(ByteBuffer data, int width, int height) {
|
||||
mImageSize = new IntSize(width, height);
|
||||
if (IntSize.isPowerOfTwo(width) && IntSize.isPowerOfTwo(height)) {
|
||||
mBufferSize = mImageSize;
|
||||
mHasImage = true;
|
||||
mImage.setBitmap(data, width, height, CairoImage.FORMAT_RGB16_565);
|
||||
} else {
|
||||
Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||
b.copyPixelsFromBuffer(data);
|
||||
setBitmap(b);
|
||||
}
|
||||
}
|
||||
|
||||
void setBitmap(Bitmap bitmap) {
|
||||
mImageSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
|
||||
int width = IntSize.nextPowerOfTwo(bitmap.getWidth());
|
||||
|
@ -116,7 +130,17 @@ public class ScreenshotLayer extends SingleTileLayer {
|
|||
}
|
||||
}
|
||||
|
||||
void setBitmap(Bitmap bitmap, int width, int height, int format) {
|
||||
void copyBuffer(ByteBuffer src, ByteBuffer dst, int size) {
|
||||
dst.asIntBuffer().put(src.asIntBuffer());
|
||||
}
|
||||
|
||||
synchronized void setBitmap(ByteBuffer data, int width, int height, int format) {
|
||||
mSize = new IntSize(width, height);
|
||||
mFormat = format;
|
||||
copyBuffer(data, mBuffer, width * height * 2);
|
||||
}
|
||||
|
||||
synchronized void setBitmap(Bitmap bitmap, int width, int height, int format) {
|
||||
Bitmap tmp;
|
||||
mSize = new IntSize(width, height);
|
||||
mFormat = format;
|
||||
|
@ -138,10 +162,10 @@ public class ScreenshotLayer extends SingleTileLayer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBuffer() { return mBuffer; }
|
||||
synchronized public ByteBuffer getBuffer() { return mBuffer; }
|
||||
@Override
|
||||
public IntSize getSize() { return mSize; }
|
||||
synchronized public IntSize getSize() { return mSize; }
|
||||
@Override
|
||||
public int getFormat() { return mFormat; }
|
||||
synchronized public int getFormat() { return mFormat; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2348,9 +2348,13 @@ jobject JNICALL
|
|||
Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
|
||||
|
||||
|
||||
nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token)
|
||||
nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstX, PRInt32 dstY, PRInt32 dstW, PRInt32 dstH, PRInt32 bufW, PRInt32 bufH, PRInt32 tabId, PRInt32 token, jobject buffer)
|
||||
{
|
||||
nsresult rv;
|
||||
float scale = 1.0;
|
||||
|
||||
if (!buffer)
|
||||
return NS_OK;
|
||||
|
||||
// take a screenshot, as wide as possible, proportional to the destination size
|
||||
if (!srcW && !srcH) {
|
||||
|
@ -2415,21 +2419,17 @@ nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt
|
|||
nsPresContext::CSSPixelsToAppUnits(srcW / scale),
|
||||
nsPresContext::CSSPixelsToAppUnits(srcH / scale));
|
||||
|
||||
PRUint32 stride = dstW * 2;
|
||||
PRUint32 bufferSize = dstH * stride;
|
||||
|
||||
jobject buffer = Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(env, NULL, bufferSize);
|
||||
if (!buffer)
|
||||
return NS_OK;
|
||||
PRUint32 stride = bufW * 2 /* 16 bpp */;
|
||||
|
||||
void* data = env->GetDirectBufferAddress(buffer);
|
||||
memset(data, 0, bufferSize);
|
||||
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(dstW, dstH), stride, gfxASurface::ImageFormatRGB16_565);
|
||||
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(bufW, bufH), stride, gfxASurface::ImageFormatRGB16_565);
|
||||
nsRefPtr<gfxContext> context = new gfxContext(surf);
|
||||
gfxPoint pt(dstX, dstY);
|
||||
context->Translate(pt);
|
||||
context->Scale(scale * dstW / srcW, scale * dstH / srcH);
|
||||
rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, srcX * dstW / srcW , srcY * dstH / srcH, dstW, dstH, token);
|
||||
env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, srcX * dstW / srcW , srcY * dstH / srcH, bufW, bufH, token);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ public:
|
|||
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);
|
||||
nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstY, PRInt32 dstX, PRInt32 dstW, PRInt32 dstH, PRInt32 bufW, PRInt32 bufH, PRInt32 tabId, PRInt32 token, jobject buffer);
|
||||
|
||||
static void NotifyPaintedRect(float top, float left, float bottom, float right);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ jfieldID AndroidGeckoEvent::jLocationField = 0;
|
|||
jfieldID AndroidGeckoEvent::jBandwidthField = 0;
|
||||
jfieldID AndroidGeckoEvent::jCanBeMeteredField = 0;
|
||||
jfieldID AndroidGeckoEvent::jScreenOrientationField = 0;
|
||||
jfieldID AndroidGeckoEvent::jByteBufferField = 0;
|
||||
|
||||
jclass AndroidPoint::jPointClass = 0;
|
||||
jfieldID AndroidPoint::jXField = 0;
|
||||
|
@ -103,6 +104,12 @@ jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
|
|||
#define getMethod(fname, ftype) \
|
||||
((jmethodID) jEnv->GetMethodID(jClass, fname, ftype))
|
||||
|
||||
RefCountedJavaObject::~RefCountedJavaObject() {
|
||||
if (mObject)
|
||||
GetJNIForThread()->DeleteGlobalRef(mObject);
|
||||
mObject = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
|
||||
{
|
||||
|
@ -154,6 +161,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
|
|||
jBandwidthField = getField("mBandwidth", "D");
|
||||
jCanBeMeteredField = getField("mCanBeMetered", "Z");
|
||||
jScreenOrientationField = getField("mScreenOrientation", "S");
|
||||
jByteBufferField = getField("mBuffer", "Ljava/nio/ByteBuffer;");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -498,7 +506,8 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
|||
case SCREENSHOT: {
|
||||
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
|
||||
mFlags = jenv->GetIntField(jobj, jFlagsField);
|
||||
ReadPointArray(mPoints, jenv, jPoints, 4);
|
||||
ReadPointArray(mPoints, jenv, jPoints, 5);
|
||||
mByteBuffer = new RefCountedJavaObject(jenv, jenv->GetObjectField(jobj, jByteBufferField));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,27 @@ void InitAndroidJavaWrappers(JNIEnv *jEnv);
|
|||
* handle it.
|
||||
*/
|
||||
|
||||
class RefCountedJavaObject {
|
||||
public:
|
||||
RefCountedJavaObject(JNIEnv* env, jobject obj) : mObject(env->NewGlobalRef(obj)) {}
|
||||
|
||||
~RefCountedJavaObject();
|
||||
|
||||
PRInt32 AddRef() { return ++mRefCnt; }
|
||||
|
||||
PRInt32 Release() {
|
||||
PRInt32 refcnt = --mRefCnt;
|
||||
if (refcnt == 0)
|
||||
delete this;
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
jobject GetObject() { return mObject; }
|
||||
private:
|
||||
PRInt32 mRefCnt;
|
||||
jobject mObject;
|
||||
};
|
||||
|
||||
class WrappedJavaObject {
|
||||
public:
|
||||
WrappedJavaObject() :
|
||||
|
@ -576,6 +597,7 @@ public:
|
|||
double Bandwidth() { return mBandwidth; }
|
||||
bool CanBeMetered() { return mCanBeMetered; }
|
||||
short ScreenOrientation() { return mScreenOrientation; }
|
||||
RefCountedJavaObject* ByteBuffer() { return mByteBuffer; }
|
||||
|
||||
protected:
|
||||
int mAction;
|
||||
|
@ -600,6 +622,7 @@ protected:
|
|||
double mBandwidth;
|
||||
bool mCanBeMetered;
|
||||
short mScreenOrientation;
|
||||
nsRefPtr<RefCountedJavaObject> mByteBuffer;
|
||||
|
||||
void ReadIntArray(nsTArray<int> &aVals,
|
||||
JNIEnv *jenv,
|
||||
|
@ -653,6 +676,7 @@ protected:
|
|||
static jfieldID jCanBeMeteredField;
|
||||
|
||||
static jfieldID jScreenOrientationField;
|
||||
static jfieldID jByteBufferField;
|
||||
|
||||
public:
|
||||
enum {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
// Make sure the order of included headers
|
||||
#include "base/basictypes.h"
|
||||
#include "nspr/prtypes.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "base/task.h"
|
||||
|
||||
#include "mozilla/Hal.h"
|
||||
#include "nsAppShell.h"
|
||||
|
@ -63,6 +65,34 @@ nsAppShell *nsAppShell::gAppShell = nsnull;
|
|||
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
|
||||
|
||||
class ScreenshotRunnable : public nsRunnable {
|
||||
public:
|
||||
ScreenshotRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId, nsTArray<nsIntPoint>& aPoints, int aToken, RefCountedJavaObject* aBuffer):
|
||||
mBrowserApp(aBrowserApp), mTabId(aTabId), mPoints(aPoints), mToken(aToken), mBuffer(aBuffer) {}
|
||||
|
||||
virtual nsresult Run() {
|
||||
nsCOMPtr<nsIDOMWindow> domWindow;
|
||||
nsCOMPtr<nsIBrowserTab> tab;
|
||||
mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
|
||||
if (!tab)
|
||||
return NS_OK;
|
||||
|
||||
tab->GetWindow(getter_AddRefs(domWindow));
|
||||
if (!domWindow)
|
||||
return NS_OK;
|
||||
|
||||
NS_ASSERTION(mPoints.Length() == 5, "Screenshot event does not have enough coordinates");
|
||||
|
||||
AndroidBridge::Bridge()->TakeScreenshot(domWindow, mPoints[0].x, mPoints[0].y, mPoints[1].x, mPoints[1].y, mPoints[2].x, mPoints[2].y, mPoints[3].x, mPoints[3].y, mPoints[4].x, mPoints[4].y, mTabId, mToken, mBuffer->GetObject());
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
|
||||
nsTArray<nsIntPoint> mPoints;
|
||||
int mTabId, mToken;
|
||||
nsRefPtr<RefCountedJavaObject> mBuffer;
|
||||
};
|
||||
|
||||
class AfterPaintListener : public nsIDOMEventListener {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -89,23 +119,15 @@ class AfterPaintListener : public nsIDOMEventListener {
|
|||
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);
|
||||
}
|
||||
nsCOMPtr<nsIDOMClientRect> rect;
|
||||
paintEvent->GetBoundingClientRect(getter_AddRefs(rect));
|
||||
float top, left, bottom, right;
|
||||
rect->GetTop(&top);
|
||||
rect->GetLeft(&left);
|
||||
rect->GetRight(&right);
|
||||
rect->GetBottom(&bottom);
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoScreenshot", "rect: %f, %f, %f, %f", top, left, right, bottom);
|
||||
AndroidBridge::NotifyPaintedRect(top, left, bottom, right);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -425,21 +447,13 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
|||
break;
|
||||
|
||||
PRInt32 token = curEvent->Flags();
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> domWindow;
|
||||
nsCOMPtr<nsIBrowserTab> tab;
|
||||
mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab));
|
||||
if (!tab)
|
||||
break;
|
||||
|
||||
tab->GetWindow(getter_AddRefs(domWindow));
|
||||
if (!domWindow)
|
||||
break;
|
||||
|
||||
float scale = 1.0;
|
||||
PRInt32 tabId = curEvent->MetaState();
|
||||
nsTArray<nsIntPoint> points = curEvent->Points();
|
||||
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());
|
||||
RefCountedJavaObject* buffer = curEvent->ByteBuffer();
|
||||
nsCOMPtr<ScreenshotRunnable> sr =
|
||||
new ScreenshotRunnable(mBrowserApp, tabId, points, token, buffer);
|
||||
MessageLoop::current()->PostIdleTask(
|
||||
FROM_HERE, NewRunnableMethod(sr.get(), &ScreenshotRunnable::Run));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче