Skip to content

Commit

Permalink
Add thumbnail generation. (mozilla-services#3282)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenba committed Oct 27, 2017
1 parent fa64832 commit 919fc3d
Showing 1 changed file with 127 additions and 4 deletions.
131 changes: 127 additions & 4 deletions addon/webextension/background/takeshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ this.takeshot = (function() {
shot.favicon = sender.tab.favIconUrl;
let capturePromise = Promise.resolve();
let openedTab;
let thumbnailBlob;
if (!shot.clipNames().length) {
// canvas.drawWindow isn't available, so we fall back to captureVisibleTab
capturePromise = screenshotPage(selectedPos, scroll).then((dataUrl) => {
Expand All @@ -33,8 +34,9 @@ this.takeshot = (function() {
}
let convertBlobPromise = Promise.resolve();
if (buildSettings.uploadBinary && !imageBlob) {
imageBlob = blobConverters.dataUrlToBlob(shot.getClip(shot.clipNames()[0]).image.url);
shot.getClip(shot.clipNames()[0]).image.url = "";
let clipImage = shot.getClip(shot.clipNames()[0]).image
imageBlob = blobConverters.dataUrlToBlob(clipImage.url);
clipImage.url = "";
} else if (!buildSettings.uploadBinary && imageBlob) {
convertBlobPromise = blobConverters.blobToDataUrl(imageBlob).then((dataUrl) => {
shot.getClip(shot.clipNames()[0]).image.url = dataUrl;
Expand All @@ -53,11 +55,19 @@ this.takeshot = (function() {
}
return catcher.watchPromise(capturePromise.then(() => {
return convertBlobPromise;
}).then(() => {
return createThumbnail(shot, imageBlob);
}).then((thumbnailImage) => {
if (buildSettings.uploadBinary) {
thumbnailBlob = thumbnailImage;
} else {
// @TODO add thumbnail to json payload
}
}).then(() => {
return browser.tabs.create({url: shot.creatingUrl})
}).then((tab) => {
openedTab = tab;
return uploadShot(shot, imageBlob);
return uploadShot(shot, imageBlob, thumbnailBlob);
}).then(() => {
return browser.tabs.update(openedTab.id, {url: shot.viewUrl}).then(
null,
Expand Down Expand Up @@ -120,6 +130,118 @@ this.takeshot = (function() {
}));
}

// x: 210
// y: 140 - 280
function createThumbnail(shot, imageBlob) {
const clipImage = shot.getClip(shot.clipNames()[0]).image;
const maxThumbnailWidth = 210 * window.devicePixelRatio;
const maxThumbnailHeight = 280 * window.devicePixelRatio;

if (clipImage.dimensions.x <= maxThumbnailWidth &&
clipImage.dimensions.y <= maxThumbnailHeight) {
// Do not create a thumbnail.
return Promise.resolve(null);
}

const minThumbnailHeight = 140 * window.devicePixelRatio;
let thumbnailImageWidth, thumbnailImageHeight,
scaledX, scaledY;
const xScaleFactor = maxThumbnailWidth * 1.0 / clipImage.dimensions.x;
scaledY = Math.round(xScaleFactor * clipImage.dimensions.y);

if (clipImage.dimensions.x > maxThumbnailWidth) {
if (scaledY < minThumbnailHeight) {
if (clipImage.dimensions.y < minThumbnailHeight) {
// CSS will scale up on y. Crop on x.
thumbnailImageHeight = scaledY = clipImage.dimensions.y;
const yScaleFactor = minThumbnailHeight * 1.0 / clipImage.dimensions.y;
scaledX = clipImage.dimensions.x;
thumbnailImageWidth = Math.min(clipImage.dimensions.x, Math.round(maxThumbnailWidth / yScaleFactor));
} else if (clipImage.dimensions.y > maxThumbnailHeight) {
// Scale on y, crop on x.
thumbnailImageHeight = scaledY = maxThumbnailHeight;
const yScaleFactor = maxThumbnailHeight * 1.0 / clipImage.dimensions.y;
scaledX = Math.round(clipImage.dimensions.x * yScaleFactor);
thumbnailImageWidth = Math.min(scaledX, maxThumbnailWidth);
} else {
// Height within limits. Crop on x.
thumbnailImageHeight = scaledY = clipImage.dimensions.y;
scaledX = clipImage.dimensions.x;
thumbnailImageWidth = Math.min(clipImage.dimensions.x, maxThumbnailWidth);
}
} else {
// Scale on x, crop on y.
thumbnailImageWidth = scaledX = maxThumbnailWidth;
thumbnailImageHeight = Math.min(scaledY, maxThumbnailHeight);
}
} else {
// The css will widen the image. Crop on y.
thumbnailImageWidth = scaledX = clipImage.dimensions.x;
scaledY = clipImage.dimensions.y;
thumbnailImageHeight = Math.min(clipImage.dimensions.y, Math.round(maxThumbnailHeight / xScaleFactor));
}

let createThumbnailFromDataUrl = function(dataUrl) {
return new Promise((resolve, reject) => {
let thumbnailImage = new Image();
let srcWidth = clipImage.dimensions.x;
let srcHeight = clipImage.dimensions.y;
let destWidth, destHeight;

thumbnailImage.onload = function() {
// Resize down 1/2 at a time produces better image quality.
// Not quite as good as using a third-party filter (which will be
// slower), but good enough.
const maxResizeScale = 0.5
destWidth = Math.round(srcWidth * maxResizeScale);
destHeight = Math.round(srcHeight * maxResizeScale);
if (destWidth <= scaledX || destHeight <= scaledY) {
srcWidth = Math.round(srcWidth * (thumbnailImageWidth * 1.0 / scaledX));
srcHeight = Math.round(srcHeight * (thumbnailImageHeight * 1.0 / scaledY));
destWidth = thumbnailImageWidth;
destHeight = thumbnailImageHeight;
}

const thumbnailCanvas = document.createElement('canvas');
thumbnailCanvas.width = destWidth;
thumbnailCanvas.height = destHeight;
const ctx = thumbnailCanvas.getContext("2d");
ctx.imageSmoothingEnabled = false;

ctx.drawImage(
thumbnailImage,
0, 0, srcWidth, srcHeight,
0, 0, destWidth, destHeight);

if (thumbnailCanvas.width <= thumbnailImageWidth ||
thumbnailCanvas.height <= thumbnailImageHeight) {
if (imageBlob) {
thumbnailCanvas.toBlob((blob) => {
resolve(blob);
});
} else {
resolve(thumbnailCanvas.toDataURL("image/png"))
}
return;
}

srcWidth = destWidth;
srcHeight = destHeight;
thumbnailImage.src = thumbnailCanvas.toDataURL();
}
thumbnailImage.src = dataUrl;
});
}

if (imageBlob) {
return blobConverters.blobToDataUrl(imageBlob).then(imageDataUrl => {
return createThumbnailFromDataUrl(imageDataUrl);
});
}

return createThumbnailFromDataUrl(clipImage.url);
}

/** Combines two buffers or Uint8Array's */
function concatBuffers(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
Expand Down Expand Up @@ -162,8 +284,9 @@ this.takeshot = (function() {
});
}

function uploadShot(shot, blob) {
function uploadShot(shot, blob, thumbnail) {
let headers;
// @TODO: add thumbnail to payload
return auth.authHeaders().then((_headers) => {
headers = _headers;
if (blob) {
Expand Down

0 comments on commit 919fc3d

Please # to comment.