experimental fix for https://github.com/vector-im/riot-web/issues/2985
needs server to support 1600x1200 thumbnails for retina large ones. ideally need to cap maximum thumbnail size to 800x600 rather than expand to arbitrary widths. need to check that luke's funky timeline code doesn't get confused between naturalWidth and infoWidth etc. also need to consider whether to encode a resolution metric in the event rather than lying about resolution.
This commit is contained in:
parent
2a20d9d67e
commit
577c411a39
3 changed files with 40 additions and 5 deletions
|
@ -81,6 +81,7 @@
|
||||||
"matrix-js-sdk": "0.14.2",
|
"matrix-js-sdk": "0.14.2",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
"pako": "^1.0.5",
|
"pako": "^1.0.5",
|
||||||
|
"png-chunks-extract": "^1.0.0",
|
||||||
"prop-types": "^15.5.8",
|
"prop-types": "^15.5.8",
|
||||||
"qrcode-react": "^0.1.16",
|
"qrcode-react": "^0.1.16",
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { _t } from './languageHandler';
|
||||||
const Modal = require('./Modal');
|
const Modal = require('./Modal');
|
||||||
|
|
||||||
const encrypt = require("browser-encrypt-attachment");
|
const encrypt = require("browser-encrypt-attachment");
|
||||||
|
const png_chunks_extract = require("png-chunks-extract");
|
||||||
|
|
||||||
// Polyfill for Canvas.toBlob API using Canvas.toDataURL
|
// Polyfill for Canvas.toBlob API using Canvas.toDataURL
|
||||||
require("blueimp-canvas-to-blob");
|
require("blueimp-canvas-to-blob");
|
||||||
|
@ -32,6 +33,9 @@ require("blueimp-canvas-to-blob");
|
||||||
const MAX_WIDTH = 800;
|
const MAX_WIDTH = 800;
|
||||||
const MAX_HEIGHT = 600;
|
const MAX_HEIGHT = 600;
|
||||||
|
|
||||||
|
// scraped out of a macOS hidpi (5660ppm) screenshot png
|
||||||
|
// 5669 px (x-axis) , 5669 px (y-axis) , per metre
|
||||||
|
const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a thumbnail for a image DOM element.
|
* Create a thumbnail for a image DOM element.
|
||||||
|
@ -102,10 +106,34 @@ function loadImageElement(imageFile) {
|
||||||
const objectUrl = URL.createObjectURL(imageFile);
|
const objectUrl = URL.createObjectURL(imageFile);
|
||||||
img.src = objectUrl;
|
img.src = objectUrl;
|
||||||
|
|
||||||
|
// check for hi-dpi PNGs and fudge display resolution as needed.
|
||||||
|
// this is mainly needed for macOS screencaps
|
||||||
|
let hidpi = false;
|
||||||
|
if (imageFile.type === "image/png") {
|
||||||
|
// in practice macOS happens to order the chunks so they fall in
|
||||||
|
// the first 0x1000 bytes (thanks to a massive ICC header).
|
||||||
|
// Thus we could slice the file down to only sniff the first 0x1000
|
||||||
|
// bytes (but this makes png_chunks_extract choke on the corrupt file)
|
||||||
|
const headers = imageFile; //.slice(0, 0x1000);
|
||||||
|
readFileAsArrayBuffer(headers).then(arrayBuffer=>{
|
||||||
|
const buffer = new Uint8Array(arrayBuffer);
|
||||||
|
const chunks = png_chunks_extract(buffer);
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
if (chunk.name === 'pHYs') {
|
||||||
|
if (chunk.data.byteLength !== PHYS_HIDPI.length) return;
|
||||||
|
hidpi = chunk.data.every((val, i) => val === PHYS_HIDPI[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Once ready, create a thumbnail
|
// Once ready, create a thumbnail
|
||||||
img.onload = function() {
|
img.onload = function() {
|
||||||
URL.revokeObjectURL(objectUrl);
|
URL.revokeObjectURL(objectUrl);
|
||||||
deferred.resolve(img);
|
let width = hidpi ? (img.width >> 1) : img.width;
|
||||||
|
let height = hidpi ? (img.height >> 1) : img.height;
|
||||||
|
deferred.resolve({ img, width, height });
|
||||||
};
|
};
|
||||||
img.onerror = function(e) {
|
img.onerror = function(e) {
|
||||||
deferred.reject(e);
|
deferred.reject(e);
|
||||||
|
@ -129,8 +157,8 @@ function infoForImageFile(matrixClient, roomId, imageFile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageInfo;
|
let imageInfo;
|
||||||
return loadImageElement(imageFile).then(function(img) {
|
return loadImageElement(imageFile).then(function(r) {
|
||||||
return createThumbnail(img, img.width, img.height, thumbnailType);
|
return createThumbnail(r.img, r.width, r.height, thumbnailType);
|
||||||
}).then(function(result) {
|
}).then(function(result) {
|
||||||
imageInfo = result.info;
|
imageInfo = result.info;
|
||||||
return uploadFile(matrixClient, roomId, result.thumbnail);
|
return uploadFile(matrixClient, roomId, result.thumbnail);
|
||||||
|
|
|
@ -179,10 +179,16 @@ export default class MImageBody extends React.Component {
|
||||||
// given we deliberately don't thumbnail them serverside to prevent
|
// given we deliberately don't thumbnail them serverside to prevent
|
||||||
// billion lol attacks and similar
|
// billion lol attacks and similar
|
||||||
return this.context.matrixClient.mxcUrlToHttp(
|
return this.context.matrixClient.mxcUrlToHttp(
|
||||||
content.info.thumbnail_url, 800, 600,
|
content.info.thumbnail_url,
|
||||||
|
800 * window.devicePixelRatio,
|
||||||
|
600 * window.devicePixelRatio,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return this.context.matrixClient.mxcUrlToHttp(content.url, 800, 600);
|
return this.context.matrixClient.mxcUrlToHttp(
|
||||||
|
content.url,
|
||||||
|
800 * window.devicePixelRatio,
|
||||||
|
600 * window.devicePixelRatio
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue