Bug 750846 - Move thumbnail size logic to Gecko. r=blassey
authorBrian Nicholson <bnicholson@mozilla.com>
Thu, 03 May 2012 16:29:59 -0700
changeset 93171 860d97778e2147750f3c2457955c322790fa03b1
parent 93170 197b42e1b32804cd041cdea60b313992cafc07b1
child 93172 05d1ae4caf36dbaf6b8e723ab12750a0409fe5c4
push id9016
push userbnicholson@mozilla.com
push dateFri, 04 May 2012 18:12:54 +0000
treeherdermozilla-inbound@860d97778e21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs750846
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 750846 - Move thumbnail size logic to Gecko. r=blassey
mobile/android/base/GeckoApp.java
mobile/android/base/Tab.java
widget/android/AndroidBridge.cpp
widget/android/nsAppShell.cpp
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -549,49 +549,48 @@ abstract public class GeckoApp
             outState.putString(SAVED_STATE_TITLE, tab.getDisplayTitle());
         outState.putBoolean(SAVED_STATE_SESSION, true);
     }
 
     void getAndProcessThumbnailForTab(final Tab tab) {
         boolean isSelectedTab = Tabs.getInstance().isSelectedTab(tab);
         final Bitmap bitmap = isSelectedTab ? mLayerClient.getBitmap() : null;
         
+        if ("about:home".equals(tab.getURL())) {
+            tab.updateThumbnail(null);
+            return;
+        }
+
         if (bitmap != null) {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
             processThumbnail(tab, bitmap, bos.toByteArray());
         } else {
             if (tab.getState() == Tab.STATE_DELAYED) {
                 byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL());
                 if (thumbnail != null)
                     processThumbnail(tab, null, thumbnail);
                 return;
             }
 
-            int sw = tab.getMinScreenshotWidth();
-            int sh = tab.getMinScreenshotHeight();
             int dw = tab.getThumbnailWidth();
             int dh = tab.getThumbnailHeight();
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, sw, sh, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
         }
     }
     
     void processThumbnail(Tab thumbnailTab, Bitmap bitmap, byte[] compressed) {
         if (Tabs.getInstance().isSelectedTab(thumbnailTab)) {
             if (compressed == null) {
                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
                 bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
                 compressed = bos.toByteArray();
             }
         }
 
-        if ("about:home".equals(thumbnailTab.getURL())) {
-            thumbnailTab.updateThumbnail(null);
-            return;
-        }
         try {
             if (bitmap == null)
                 bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
             thumbnailTab.updateThumbnail(bitmap);
         } catch (OutOfMemoryError ome) {
             Log.w(LOGTAG, "decoding byte array ran out of memory", ome);
         }
     }
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -62,20 +62,17 @@ import java.util.List;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
 public final class Tab {
     private static final String LOGTAG = "GeckoTab";
     private static final int kThumbnailWidth = 136;
     private static final int kThumbnailHeight = 78;
 
-    private static float sMinDim = 0;
-    private static float sDensity = 1;
-    private static int sMinScreenshotWidth = 0;
-    private static int sMinScreenshotHeight = 0;
+    private static float sDensity = 0.0f;
     private static Pattern sColorPattern;
     private int mId;
     private String mUrl;
     private String mTitle;
     private Drawable mFavicon;
     private String mFaviconUrl;
     private JSONObject mIdentityData;
     private Drawable mThumbnail;
@@ -173,87 +170,46 @@ public final class Tab {
     public Drawable getFavicon() {
         return mFavicon;
     }
 
     public Drawable getThumbnail() {
         return mThumbnail;
     }
 
-    void initMetrics() {
-        DisplayMetrics metrics = new DisplayMetrics();
-        GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-        sMinDim = Math.min(metrics.widthPixels / kThumbnailWidth, metrics.heightPixels / kThumbnailHeight);
-        sDensity = metrics.density;
-    }
-
-    float getMinDim() {
-        if (sMinDim == 0)
-            initMetrics();
-        return sMinDim;
-    }
-
     float getDensity() {
-        if (sDensity == 0.0f)
-            initMetrics();
+        if (sDensity == 0.0f) {
+            DisplayMetrics metrics = new DisplayMetrics();
+            GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+            sDensity = metrics.density;
+        }
         return sDensity;
     }
 
-    int getMinScreenshotWidth() {
-        if (sMinScreenshotWidth != 0)
-            return sMinScreenshotWidth;
-        return sMinScreenshotWidth = (int)(getMinDim() * kThumbnailWidth);
-    }
-
-    int getMinScreenshotHeight() {
-        if (sMinScreenshotHeight != 0)
-            return sMinScreenshotHeight;
-        return sMinScreenshotHeight = (int)(getMinDim() * kThumbnailHeight);
-    }
-
     int getThumbnailWidth() {
         return (int)(kThumbnailWidth * getDensity());
     }
 
     int getThumbnailHeight() {
         return (int)(kThumbnailHeight * getDensity());
     }
 
     public void updateThumbnail(final Bitmap b) {
         final Tab tab = this;
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
                 if (b != null) {
                     try {
-                        Bitmap cropped = null;
-                        /* Crop to screen width if the bitmap is larger than the screen width or height. If smaller and the
-                         * the aspect ratio is correct, just use the bitmap as is. Otherwise, fit the smaller
-                         * smaller dimension, then crop the larger dimention.
-                         */
-                        if (getMinScreenshotWidth() < b.getWidth() && getMinScreenshotHeight() < b.getHeight())
-                            cropped = Bitmap.createBitmap(b, 0, 0, getMinScreenshotWidth(), getMinScreenshotHeight());
-                        else if (b.getWidth() * getMinScreenshotHeight() == b.getHeight() * getMinScreenshotWidth())
-                            cropped = b;
-                        else if (b.getWidth() * getMinScreenshotHeight() < b.getHeight() * getMinScreenshotWidth())
-                            cropped = Bitmap.createBitmap(b, 0, 0, b.getWidth(), 
-                                                          b.getWidth() * getMinScreenshotHeight() / getMinScreenshotWidth());
-                        else
-                            cropped = Bitmap.createBitmap(b, 0, 0, 
-                                                          b.getHeight() * getMinScreenshotWidth() / getMinScreenshotHeight(),
-                                                          b.getHeight());
-
-                        Bitmap bitmap = Bitmap.createScaledBitmap(cropped, getThumbnailWidth(), getThumbnailHeight(), false);
+                        Bitmap bitmap = Bitmap.createScaledBitmap(b, getThumbnailWidth(), getThumbnailHeight(), false);
 
                         if (mState == Tab.STATE_SUCCESS)
                             saveThumbnailToDB(new BitmapDrawable(bitmap));
 
-                        if (!cropped.equals(b))
-                            b.recycle();
                         mThumbnail = new BitmapDrawable(bitmap);
-                        cropped.recycle();
+                        b.recycle();
                     } catch (OutOfMemoryError oom) {
                         Log.e(LOGTAG, "Unable to create/scale bitmap", oom);
                         mThumbnail = null;
                     }
                 } else {
                     mThumbnail = null;
                 }
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -2390,16 +2390,52 @@ AndroidBridge::RemovePluginView(jobject 
 extern "C"
 __attribute__ ((visibility("default")))
 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 rv;
+
+    // take a screenshot, as wide as possible, proportional to the destination size
+    if (!srcW && !srcH) {
+        nsCOMPtr<nsIDOMDocument> doc;
+        rv = window->GetDocument(getter_AddRefs(doc));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr<nsIDOMElement> docElement;
+        rv = doc->GetDocumentElement(getter_AddRefs(docElement));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        PRInt32 viewportHeight;
+        PRInt32 pageWidth;
+        PRInt32 pageHeight;
+        window->GetInnerHeight(&viewportHeight);
+        docElement->GetScrollWidth(&pageWidth);
+        docElement->GetScrollHeight(&pageHeight);
+
+        // use the page or viewport dimensions, whichever is larger
+        PRInt32 width = pageWidth;
+        PRInt32 height = viewportHeight > pageHeight ? viewportHeight : pageHeight;
+
+        if (!width || !height)
+            return NS_ERROR_FAILURE;
+
+        float aspectRatio = ((float) dstW) / dstH;
+        if (width / aspectRatio < height) {
+            srcW = width;
+            srcH = width / aspectRatio;
+        } else {
+            srcW = height * aspectRatio;
+            srcH = height;
+        }
+    }
+
     JNIEnv* env = GetJNIEnv();
     if (!env)
         return NS_OK;
 
     AutoLocalJNIFrame jniFrame(env);
 
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
     if (!win)
@@ -2427,17 +2463,17 @@ nsresult AndroidBridge::TakeScreenshot(n
     if (!buffer)
         return NS_OK;
 
     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<gfxContext> context = new gfxContext(surf);
     context->Scale(scale * dstW / srcW, scale * dstH / srcH);
-    nsresult rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
+    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);
     return NS_OK;
 }
 
 void
 AndroidBridge::NotifyPaintedRect(float top, float left, float bottom, float right)
 {
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -465,21 +465,16 @@ nsAppShell::ProcessNextNativeEvent(bool 
         if (!tab)
             break;
 
         tab->GetWindow(getter_AddRefs(domWindow));
         if (!domWindow)
             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");
         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;
     }
 
     case AndroidGeckoEvent::VIEWPORT:
     case AndroidGeckoEvent::BROADCAST: {