Merge pull request #2439 from matrix-org/matthew/retina

Fix for retina thumbnails being massive
This commit is contained in:
Bruno Windels 2019-04-09 16:14:25 +00:00 committed by GitHub
commit 0592a1711a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 19 deletions

View file

@ -150,7 +150,7 @@ export default class MImageBody extends React.Component {
if (this.refs.image) {
const { naturalWidth, naturalHeight } = this.refs.image;
// this is only used as a fallback in case content.info.w/h is missing
loadedImageDimensions = { naturalWidth, naturalHeight };
}
@ -167,6 +167,14 @@ export default class MImageBody extends React.Component {
}
_getThumbUrl() {
// FIXME: the dharma skin lets images grow as wide as you like, rather than capped to 800x600.
// So either we need to support custom timeline widths here, or reimpose the cap, otherwise the
// thumbnail resolution will be unnecessarily reduced.
// custom timeline widths seems preferable.
const pixelRatio = window.devicePixelRatio;
const thumbWidth = 800 * pixelRatio;
const thumbHeight = 600 * pixelRatio;
const content = this.props.mxEvent.getContent();
if (content.file !== undefined) {
// Don't use the thumbnail for clients wishing to autoplay gifs.
@ -175,14 +183,61 @@ export default class MImageBody extends React.Component {
}
return this.state.decryptedUrl;
} else if (content.info && content.info.mimetype === "image/svg+xml" && content.info.thumbnail_url) {
// special case to return client-generated thumbnails for SVGs, if any,
// special case to return clientside sender-generated thumbnails for SVGs, if any,
// given we deliberately don't thumbnail them serverside to prevent
// billion lol attacks and similar
return this.context.matrixClient.mxcUrlToHttp(
content.info.thumbnail_url, 800, 600,
content.info.thumbnail_url,
thumbWidth,
thumbHeight,
);
} else {
return this.context.matrixClient.mxcUrlToHttp(content.url, 800, 600);
// we try to download the correct resolution
// for hi-res images (like retina screenshots).
// synapse only supports 800x600 thumbnails for now though,
// so we'll need to download the original image for this to work
// well for now. First, let's try a few cases that let us avoid
// downloading the original:
if (pixelRatio === 1.0 ||
(!content.info || !content.info.w ||
!content.info.h || !content.info.size)) {
// always thumbnail. it may look a bit worse, but it'll save bandwidth.
// which is probably desirable on a lo-dpi device anyway.
return this.context.matrixClient.mxcUrlToHttp(content.url, thumbWidth, thumbHeight);
} else {
// we should only request thumbnails if the image is bigger than 800x600
// (or 1600x1200 on retina) otherwise the image in the timeline will just
// end up resampled and de-retina'd for no good reason.
// Ideally the server would pregen 1600x1200 thumbnails in order to provide retina
// thumbnails, but we don't do this currently in synapse for fear of disk space.
// As a compromise, let's switch to non-retina thumbnails only if the original
// image is both physically too large and going to be massive to load in the
// timeline (e.g. >1MB).
const isLargerThanThumbnail = (
content.info.w > thumbWidth ||
content.info.h > thumbHeight
);
const isLargeFileSize = content.info.size > 1*1024*1024;
if (isLargeFileSize && isLargerThanThumbnail) {
// image is too large physically and bytewise to clutter our timeline so
// we ask for a thumbnail, despite knowing that it will be max 800x600
// despite us being retina (as synapse doesn't do 1600x1200 thumbs yet).
return this.context.matrixClient.mxcUrlToHttp(
content.url,
thumbWidth,
thumbHeight,
);
} else {
// download the original image otherwise, so we can scale it client side
// to take pixelRatio into account.
// ( no width/height means we want the original image)
return this.context.matrixClient.mxcUrlToHttp(
content.url,
);
}
}
}
}