1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-11-05 23:10:41 +03:00

Scale all mxc thumbs using device pixel ratio for hidpi

as we are notoriously bad at doing it everywhere we ought to, like the TopLeftMenu avatar
This commit is contained in:
Michael Telatynski
2021-04-26 18:25:49 +01:00
parent 9401a6d6dc
commit 915f8b3c9c
8 changed files with 20 additions and 35 deletions

View File

@@ -27,11 +27,7 @@ export type ResizeMethod = "crop" | "scale";
export function avatarUrlForMember(member: RoomMember, width: number, height: number, resizeMethod: ResizeMethod) { export function avatarUrlForMember(member: RoomMember, width: number, height: number, resizeMethod: ResizeMethod) {
let url: string; let url: string;
if (member?.getMxcAvatarUrl()) { if (member?.getMxcAvatarUrl()) {
url = mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp( url = mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(width, height, resizeMethod);
Math.floor(width * window.devicePixelRatio),
Math.floor(height * window.devicePixelRatio),
resizeMethod,
);
} }
if (!url) { if (!url) {
// member can be null here currently since on invites, the JS SDK // member can be null here currently since on invites, the JS SDK
@@ -44,11 +40,7 @@ export function avatarUrlForMember(member: RoomMember, width: number, height: nu
export function avatarUrlForUser(user: User, width: number, height: number, resizeMethod?: ResizeMethod) { export function avatarUrlForUser(user: User, width: number, height: number, resizeMethod?: ResizeMethod) {
if (!user.avatarUrl) return null; if (!user.avatarUrl) return null;
return mediaFromMxc(user.avatarUrl).getThumbnailOfSourceHttp( return mediaFromMxc(user.avatarUrl).getThumbnailOfSourceHttp(width, height, resizeMethod);
Math.floor(width * window.devicePixelRatio),
Math.floor(height * window.devicePixelRatio),
resizeMethod,
);
} }
function isValidHexColor(color: string): boolean { function isValidHexColor(color: string): boolean {

View File

@@ -136,7 +136,7 @@ const Tile: React.FC<ITileProps> = ({
let url: string; let url: string;
if (room.avatar_url) { if (room.avatar_url) {
url = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(Math.floor(20 * window.devicePixelRatio)); url = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(20);
} }
let description = _t("%(count)s members", { count: room.num_joined_members }); let description = _t("%(count)s members", { count: room.num_joined_members });

View File

@@ -68,8 +68,8 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
let imageUrl = null; let imageUrl = null;
if (props.member.getMxcAvatarUrl()) { if (props.member.getMxcAvatarUrl()) {
imageUrl = mediaFromMxc(props.member.getMxcAvatarUrl()).getThumbnailOfSourceHttp( imageUrl = mediaFromMxc(props.member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(
Math.floor(props.width * window.devicePixelRatio), props.width,
Math.floor(props.height * window.devicePixelRatio), props.height,
props.resizeMethod, props.resizeMethod,
); );
} }

View File

@@ -93,8 +93,8 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
let oobAvatar = null; let oobAvatar = null;
if (props.oobData.avatarUrl) { if (props.oobData.avatarUrl) {
oobAvatar = mediaFromMxc(props.oobData.avatarUrl).getThumbnailOfSourceHttp( oobAvatar = mediaFromMxc(props.oobData.avatarUrl).getThumbnailOfSourceHttp(
Math.floor(props.width * window.devicePixelRatio), props.width,
Math.floor(props.height * window.devicePixelRatio), props.height,
props.resizeMethod, props.resizeMethod,
); );
} }
@@ -109,12 +109,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
private static getRoomAvatarUrl(props: IProps): string { private static getRoomAvatarUrl(props: IProps): string {
if (!props.room) return null; if (!props.room) return null;
return Avatar.avatarUrlForRoom( return Avatar.avatarUrlForRoom(props.room, props.width, props.height, props.resizeMethod);
props.room,
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
);
} }
private onRoomAvatarClick = () => { private onRoomAvatarClick = () => {

View File

@@ -130,7 +130,7 @@ export default class IncomingSasDialog extends React.Component {
const oppProfile = this.state.opponentProfile; const oppProfile = this.state.opponentProfile;
if (oppProfile) { if (oppProfile) {
const url = oppProfile.avatar_url const url = oppProfile.avatar_url
? mediaFromMxc(oppProfile.avatar_url).getSquareThumbnailHttp(Math.floor(48 * window.devicePixelRatio)) ? mediaFromMxc(oppProfile.avatar_url).getSquareThumbnailHttp(48)
: null; : null;
profile = <div className="mx_IncomingSasDialog_opponentProfile"> profile = <div className="mx_IncomingSasDialog_opponentProfile">
<BaseAvatar name={oppProfile.displayname} <BaseAvatar name={oppProfile.displayname}

View File

@@ -185,9 +185,8 @@ export default class MImageBody extends React.Component {
// So either we need to support custom timeline widths here, or reimpose the cap, otherwise the // So either we need to support custom timeline widths here, or reimpose the cap, otherwise the
// thumbnail resolution will be unnecessarily reduced. // thumbnail resolution will be unnecessarily reduced.
// custom timeline widths seems preferable. // custom timeline widths seems preferable.
const pixelRatio = window.devicePixelRatio; const thumbWidth = 800;
const thumbWidth = Math.round(800 * pixelRatio); const thumbHeight = 600;
const thumbHeight = Math.round(600 * pixelRatio);
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
const media = mediaFromContent(content); const media = mediaFromContent(content);

View File

@@ -96,6 +96,9 @@ export class Media {
*/ */
public getThumbnailHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null | undefined { public getThumbnailHttp(width: number, height: number, mode: ResizeMethod = "scale"): string | null | undefined {
if (!this.hasThumbnail) return null; if (!this.hasThumbnail) return null;
// scale using the device pixel ratio to keep images clear
width = Math.floor(width * window.devicePixelRatio);
height = Math.floor(height * window.devicePixelRatio);
return this.client.mxcUrlToHttp(this.thumbnailMxc, width, height, mode); return this.client.mxcUrlToHttp(this.thumbnailMxc, width, height, mode);
} }
@@ -107,6 +110,9 @@ export class Media {
* @returns {string} The HTTP URL which points to the thumbnail. * @returns {string} The HTTP URL which points to the thumbnail.
*/ */
public getThumbnailOfSourceHttp(width: number, height: number, mode: ResizeMethod = "scale"): string { public getThumbnailOfSourceHttp(width: number, height: number, mode: ResizeMethod = "scale"): string {
// scale using the device pixel ratio to keep images clear
width = Math.floor(width * window.devicePixelRatio);
height = Math.floor(height * window.devicePixelRatio);
return this.client.mxcUrlToHttp(this.srcMxc, width, height, mode); return this.client.mxcUrlToHttp(this.srcMxc, width, height, mode);
} }
@@ -117,6 +123,7 @@ export class Media {
* @returns {string} An HTTP URL for the thumbnail. * @returns {string} An HTTP URL for the thumbnail.
*/ */
public getSquareThumbnailHttp(dim: number): string { public getSquareThumbnailHttp(dim: number): string {
dim = Math.floor(dim * window.devicePixelRatio); // scale using the device pixel ratio to keep images clear
if (this.hasThumbnail) { if (this.hasThumbnail) {
return this.getThumbnailHttp(dim, dim, 'crop'); return this.getThumbnailHttp(dim, dim, 'crop');
} }

View File

@@ -341,11 +341,7 @@ class RoomPillPart extends PillPart {
setAvatar(node: HTMLElement) { setAvatar(node: HTMLElement) {
let initialLetter = ""; let initialLetter = "";
let avatarUrl = Avatar.avatarUrlForRoom( let avatarUrl = Avatar.avatarUrlForRoom(this.room, 16, 16, "crop");
this.room,
16 * window.devicePixelRatio,
16 * window.devicePixelRatio,
"crop");
if (!avatarUrl) { if (!avatarUrl) {
initialLetter = Avatar.getInitialLetter(this.room ? this.room.name : this.resourceId); initialLetter = Avatar.getInitialLetter(this.room ? this.room.name : this.resourceId);
avatarUrl = Avatar.defaultAvatarUrlForString(this.room ? this.room.roomId : this.resourceId); avatarUrl = Avatar.defaultAvatarUrlForString(this.room ? this.room.roomId : this.resourceId);
@@ -383,11 +379,7 @@ class UserPillPart extends PillPart {
} }
const name = this.member.name || this.member.userId; const name = this.member.name || this.member.userId;
const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId); const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId);
const avatarUrl = Avatar.avatarUrlForMember( const avatarUrl = Avatar.avatarUrlForMember(this.member, 16, 16, "crop");
this.member,
16 * window.devicePixelRatio,
16 * window.devicePixelRatio,
"crop");
let initialLetter = ""; let initialLetter = "";
if (avatarUrl === defaultAvatarUrl) { if (avatarUrl === defaultAvatarUrl) {
initialLetter = Avatar.getInitialLetter(name); initialLetter = Avatar.getInitialLetter(name);