Bug 1096804 - Add support to PageThumbs for requesting non-downsampled content screenshots. r=dao

This commit is contained in:
Jim Mathies 2016-01-12 11:08:33 -06:00
parent 3e43959d68
commit 0f68cf1319
5 changed files with 87 additions and 52 deletions

View File

@ -296,11 +296,14 @@ PreviewController.prototype = {
ctx.drawWindow(this.win.win, 0, 0, winWidth, winHeight, "rgba(0,0,0,0)");
// Draw the content are into the composite canvas at the right location.
ctx.drawImage(aPreviewCanvas, this.browserDims.x, this.browserDims.y, aPreviewCanvas.width, aPreviewCanvas.height);
ctx.drawImage(aPreviewCanvas, this.browserDims.x, this.browserDims.y,
aPreviewCanvas.width, aPreviewCanvas.height);
ctx.restore();
// Deliver the resulting composite canvas to Windows
this.win.tabbrowser.previewTab(this.tab, function () { aTaskbarCallback.done(composite, false); });
this.win.tabbrowser.previewTab(this.tab, function () {
aTaskbarCallback.done(composite, false);
});
});
},
@ -379,7 +382,7 @@ function TabWindow(win) {
this.win.addEventListener(this.winEvents[i], this, false);
this.tabbrowser.addTabsProgressListener(this);
AeroPeek.windows.push(this);
let tabs = this.tabbrowser.tabs;
for (let i = 0; i < tabs.length; i++)

View File

@ -139,50 +139,55 @@ this.PageThumbUtils = {
* jagged pixels to represent text.
*
* @params aWindow - the window to create a snapshot of.
* @params aDestCanvas (optional) a destination canvas to draw the final snapshot to.
* @params aDestCanvas destination canvas to draw the final
* snapshot to. Can be null.
* @param aArgs (optional) Additional named parameters:
* fullScale - request that a non-downscaled image be returned.
* @return Canvas with a scaled thumbnail of the window.
*/
createSnapshotThumbnail: function(aWindow, aDestCanvas = null) {
createSnapshotThumbnail: function(aWindow, aDestCanvas, aArgs) {
if (Cu.isCrossProcessWrapper(aWindow)) {
throw new Error('Do not pass cpows here.');
}
let fullScale = aArgs ? aArgs.fullScale : false;
let [contentWidth, contentHeight] = this.getContentSize(aWindow);
let [thumbnailWidth, thumbnailHeight] = aDestCanvas ?
[aDestCanvas.width, aDestCanvas.height] :
this.getThumbnailSize(aWindow);
// If the caller wants a fullscale image, set the desired thumbnail dims
// to the dims of content and (if provided) size the incoming canvas to
// support our results.
if (fullScale) {
thumbnailWidth = contentWidth;
thumbnailHeight = contentHeight;
if (aDestCanvas) {
aDestCanvas.width = contentWidth;
aDestCanvas.height = contentHeight;
}
}
let intermediateWidth = thumbnailWidth * 2;
let intermediateHeight = thumbnailHeight * 2;
let skipDownscale = false;
let snapshotCanvas = undefined;
// Our intermediate thumbnail is bigger than content,
// which can happen on hiDPI devices like a retina macbook pro.
// In those cases, just render at the final size.
if ((intermediateWidth >= contentWidth) ||
(intermediateHeight >= contentHeight)) {
// If the intermediate thumbnail is larger than content dims (hiDPI
// devices can experience this) or a full preview is requested render
// at the final thumbnail size.
if ((intermediateWidth >= contentWidth ||
intermediateHeight >= contentHeight) || fullScale) {
intermediateWidth = thumbnailWidth;
intermediateHeight = thumbnailHeight;
skipDownscale = true;
snapshotCanvas = aDestCanvas;
}
// If we've been given a large preallocated canvas, so
// just render once into the destination canvas.
if (aDestCanvas &&
((aDestCanvas.width >= intermediateWidth) ||
(aDestCanvas.height >= intermediateHeight))) {
intermediateWidth = aDestCanvas.width;
intermediateHeight = aDestCanvas.height;
skipDownscale = true;
snapshotCanvas = aDestCanvas;
}
// Create an intermediate surface
let snapshotCanvas = this.createCanvas(aWindow, intermediateWidth,
intermediateHeight);
if (!snapshotCanvas) {
snapshotCanvas = this.createCanvas(aWindow, intermediateWidth, intermediateHeight);
}
// This is step 1.
// Step 1: capture the image at the intermediate dims. For thumbnails
// this is twice the thumbnail size, for fullScale images this is at
// content dims.
// Also by default, canvas does not draw the scrollbars, so no need to
// remove the scrollbar sizes.
let scale = Math.min(Math.max(intermediateWidth / contentWidth,
@ -195,18 +200,21 @@ this.PageThumbUtils = {
PageThumbUtils.THUMBNAIL_BG_COLOR,
snapshotCtx.DRAWWINDOW_DO_NOT_FLUSH);
snapshotCtx.restore();
if (skipDownscale) {
return snapshotCanvas;
}
// Part 2: Assumes that the snapshot is 2x the thumbnail size
let finalCanvas = aDestCanvas || this.createCanvas(aWindow, thumbnailWidth, thumbnailHeight);
// Part 2: Downscale from our intermediate dims to the final thumbnail
// dims and copy the result to aDestCanvas. If the caller didn't
// provide a target canvas, create a new canvas and return it.
let finalCanvas = aDestCanvas ||
this.createCanvas(aWindow, thumbnailWidth, thumbnailHeight);
let finalCtx = finalCanvas.getContext("2d");
finalCtx.save();
finalCtx.scale(0.5, 0.5);
if (!skipDownscale) {
finalCtx.scale(0.5, 0.5);
}
finalCtx.drawImage(snapshotCanvas, 0, 0);
finalCtx.restore();
return finalCanvas;
},

View File

@ -207,13 +207,20 @@ this.PageThumbs = {
* canvas asynchronously. Pass aCallback to receive an async callback after
* canvas painting has completed.
* @param aBrowser The browser to capture a thumbnail from.
* @param aCanvas The canvas to draw to.
* @param aCanvas The canvas to draw to. The thumbnail will be scaled to match
* the dimensions of this canvas. If callers pass a 0x0 canvas, the canvas
* will be resized to default thumbnail dimensions just prior to painting.
* @param aCallback (optional) A callback invoked once the thumbnail has been
* rendered to aCanvas.
* rendered to aCanvas.
* @param aArgs (optional) Additional named parameters:
* fullScale - request that a non-downscaled image be returned.
*/
captureToCanvas: function PageThumbs_captureToCanvas(aBrowser, aCanvas, aCallback) {
captureToCanvas: function (aBrowser, aCanvas, aCallback, aArgs) {
let telemetryCaptureTime = new Date();
this._captureToCanvas(aBrowser, aCanvas, function () {
let args = {
fullScale: aArgs ? aArgs.fullScale : false
};
this._captureToCanvas(aBrowser, aCanvas, args, (aCanvas) => {
Services.telemetry
.getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
.add(new Date() - telemetryCaptureTime);
@ -257,14 +264,17 @@ this.PageThumbs = {
// The background thumbnail service captures to canvas but doesn't want to
// participate in this service's telemetry, which is why this method exists.
_captureToCanvas: function (aBrowser, aCanvas, aCallback) {
_captureToCanvas: function (aBrowser, aCanvas, aArgs, aCallback) {
if (aBrowser.isRemoteBrowser) {
Task.spawn(function* () {
let data =
yield this._captureRemoteThumbnail(aBrowser, aCanvas);
yield this._captureRemoteThumbnail(aBrowser, aCanvas.width,
aCanvas.height, aArgs);
let canvas = data.thumbnail;
let ctx = canvas.getContext("2d");
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
aCanvas.width = canvas.width;
aCanvas.height = canvas.height;
aCanvas.getContext("2d").putImageData(imgData, 0, 0);
if (aCallback) {
aCallback(aCanvas);
@ -272,8 +282,10 @@ this.PageThumbs = {
}.bind(this));
return;
}
aCanvas = PageThumbUtils.createSnapshotThumbnail(aBrowser.contentWindow, aCanvas);
// The content is a local page, grab a thumbnail sync.
PageThumbUtils.createSnapshotThumbnail(aBrowser.contentWindow,
aCanvas,
aArgs);
if (aCallback) {
aCallback(aCanvas);
@ -284,10 +296,13 @@ this.PageThumbs = {
* Asynchrnously render an appropriately scaled thumbnail to canvas.
*
* @param aBrowser The browser to capture a thumbnail from.
* @param aCanvas The canvas to draw to.
* @param aWidth The desired canvas width.
* @param aHeight The desired canvas height.
* @param aArgs (optional) Additional named parameters:
* fullScale - request that a non-downscaled image be returned.
* @return a promise
*/
_captureRemoteThumbnail: function (aBrowser, aCanvas) {
_captureRemoteThumbnail: function (aBrowser, aWidth, aHeight, aArgs) {
let deferred = Promise.defer();
// The index we send with the request so we can identify the
@ -329,10 +344,11 @@ this.PageThumbs = {
// Send a thumbnail request
mm.addMessageListener("Browser:Thumbnail:Response", thumbFunc);
mm.sendAsyncMessage("Browser:Thumbnail:Request", {
canvasWidth: aCanvas.width,
canvasHeight: aCanvas.height,
canvasWidth: aWidth,
canvasHeight: aHeight,
background: PageThumbUtils.THUMBNAIL_BG_COLOR,
id: index
id: index,
additionalArgs: aArgs
});
return deferred.promise;

View File

@ -133,7 +133,7 @@ const backgroundPageThumbsContent = {
let canvasDrawDate = new Date();
let finalCanvas = PageThumbUtils.createSnapshotThumbnail(content);
let finalCanvas = PageThumbUtils.createSnapshotThumbnail(content, null);
capture.canvasDrawTime = new Date() - canvasDrawDate;
finalCanvas.toBlob(blob => {

View File

@ -467,10 +467,18 @@ addMessageListener("UpdateCharacterSet", function (aMessage) {
* Remote thumbnail request handler for PageThumbs thumbnails.
*/
addMessageListener("Browser:Thumbnail:Request", function (aMessage) {
let snapshotWidth = aMessage.data.canvasWidth;
let snapshotHeight = aMessage.data.canvasHeight;
let canvas = PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight);
let snapshot = PageThumbUtils.createSnapshotThumbnail(content, canvas);
let snapshot;
let args = aMessage.data.additionalArgs;
let fullScale = args ? args.fullScale : false;
if (fullScale) {
snapshot = PageThumbUtils.createSnapshotThumbnail(content, null, args);
} else {
let snapshotWidth = aMessage.data.canvasWidth;
let snapshotHeight = aMessage.data.canvasHeight;
snapshot =
PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight);
PageThumbUtils.createSnapshotThumbnail(content, snapshot, args);
}
snapshot.toBlob(function (aBlob) {
sendAsyncMessage("Browser:Thumbnail:Response", {