refactor: use consistent preview sizes (fix #679)

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/685/head
Varun Patil 2023-06-04 10:44:35 -07:00
parent 078f4f7a5c
commit 4e283ecc93
12 changed files with 95 additions and 31 deletions

View File

@ -107,7 +107,7 @@ trait TimelineQueryMap
// SELECT these files from the filecache
$query = $this->connection->getQueryBuilder();
$query->select('m.fileid', 'm.dayid', 'm.mapcluster', 'f.etag')
$query->select('m.fileid', 'm.dayid', 'm.mapcluster', 'm.h', 'm.w', 'f.etag')
->from('memories', 'm')
->innerJoin('m', 'filecache', 'f', $query->expr()->eq('m.fileid', 'f.fileid'))
->where($query->expr()->in('m.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)))
@ -119,6 +119,8 @@ trait TimelineQueryMap
$row['fileid'] = (int) $row['fileid'];
$row['mapcluster'] = (int) $row['mapcluster'];
$row['dayid'] = (int) $row['dayid'];
$row['h'] = (int) $row['h'];
$row['w'] = (int) $row['w'];
}
return $files;

View File

@ -48,7 +48,7 @@ declare global {
var mViewer: {
open: (anchorPhoto: IPhoto, rows: IRow[]) => Promise<void>;
openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: number): Promise<void>;
openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: 256 | 512): Promise<void>;
close: () => void;
isOpen: () => boolean;
};

View File

@ -68,7 +68,7 @@ export default defineComponent({
etag: this.album.album_id,
flag: 0,
} as unknown as IPhoto;
return getPreviewUrl(mock, true, 512);
return getPreviewUrl({ photo: mock, sqsize: 512 });
}
return API.CLUSTER_PREVIEW(this.data.cluster_type, this.data.cluster_id);

View File

@ -17,7 +17,7 @@
<div class="previews fill-block">
<div class="preview-container fill-block">
<div class="img-outer" v-for="info of previews" :key="info.fileid">
<XImg class="ximg fill-block" :src="getPreviewUrl(info, true, 256)" />
<XImg class="ximg fill-block" :src="previewUrl(info)" />
</div>
</div>
</div>
@ -28,7 +28,7 @@
import { defineComponent, PropType } from 'vue';
import { IFolder, IPhoto } from '../../types';
import { getPreviewUrl } from '../../services/utils/helpers';
import * as utils from '../../services/utils/helpers';
import UserConfig from '../../mixins/UserConfig';
import FolderIcon from 'vue-material-design-icons/Folder.vue';
@ -53,8 +53,6 @@ export default defineComponent({
previews: [] as IPhoto[],
// Error occured fetching thumbs
error: false,
// Passthrough
getPreviewUrl,
}),
computed: {
@ -107,6 +105,14 @@ export default defineComponent({
}
}
},
/** Get preview url */
previewUrl(info: IPhoto) {
return utils.getPreviewUrl({
photo: info,
sqsize: 256,
});
},
},
});
</script>

View File

@ -160,7 +160,7 @@ export default defineComponent({
/** Get url of the photo */
url() {
let base = 256;
let base: 256 | 512 = 256;
// Check if displayed size is larger than the image
if (this.data.dispH! > base * 0.9 && this.data.dispW! > base * 0.9) {
@ -172,13 +172,10 @@ export default defineComponent({
base = 512;
}
// Make the shorter dimension equal to base
let size = base;
if (this.data.w && this.data.h) {
size = Math.floor((base * Math.max(this.data.w, this.data.h)) / Math.min(this.data.w, this.data.h)) - 1;
}
return utils.getPreviewUrl(this.data, false, size);
return utils.getPreviewUrl({
photo: this.data,
msize: base,
});
},
/** Set src with overlay face rect */

View File

@ -91,13 +91,12 @@ export default defineComponent({
methods: {
toCoverUrl(fileId: string | number) {
return getPreviewUrl(
{
return getPreviewUrl({
photo: {
fileid: Number(fileId),
} as IPhoto,
true,
256
);
sqsize: 256,
});
},
albumCreatedHandler() {

View File

@ -154,7 +154,10 @@ export default defineComponent({
},
async sharePreview() {
const src = utils.getPreviewUrl(this.photo!, false, 2048);
const src = utils.getPreviewUrl({
photo: this.photo!,
size: 2048,
});
this.shareWithHref(src, true);
},

View File

@ -281,7 +281,10 @@ export default defineComponent({
},
clusterPreviewUrl(cluster: IMarkerCluster) {
return utils.getPreviewUrl(cluster.preview, false, 256);
return utils.getPreviewUrl({
photo: cluster.preview,
msize: 256,
});
},
clusterIconClass(cluster: IMarkerCluster) {

View File

@ -151,7 +151,10 @@ export default defineComponent({
// Get random photo
year.preview ||= utils.randomChoice(year.photos);
year.url = utils.getPreviewUrl(year.preview, false, 512);
year.url = utils.getPreviewUrl({
photo: year.preview,
msize: 512,
});
}
await this.$nextTick();

View File

@ -49,7 +49,7 @@ export default defineComponent({
return {
source:
this.photo.h && this.photo.w
? utils.getPreviewUrl(this.photo, false, 'screen')
? utils.getPreviewUrl({ photo: this.photo, size: 'screen' })
: API.IMAGE_DECODABLE(this.photo.fileid, this.photo.etag),
defaultSavedImageName: this.defaultSavedImageName,

View File

@ -694,7 +694,8 @@ export default defineComponent({
preload(dayIdx + 3);
// Get thumb image
const thumbSrc: string = this.thumbElem(photo)?.getAttribute('src') || utils.getPreviewUrl(photo, false, 256);
const thumbSrc: string =
this.thumbElem(photo)?.getAttribute('src') || utils.getPreviewUrl({ photo, msize: 256 });
// Get full image
return {
@ -739,7 +740,7 @@ export default defineComponent({
},
/** Open with a static list of photos */
async openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: number) {
async openStatic(photo: IPhoto, list: IPhoto[], thumbSize?: 256 | 512) {
this.list = list;
const photoswipe = await this.createBase({
index: list.findIndex((p) => p.fileid === photo.fileid),
@ -750,7 +751,7 @@ export default defineComponent({
photoswipe.addFilter('itemData', (itemData, index) => ({
...this.getItemData(this.list[index]),
msrc: thumbSize ? utils.getPreviewUrl(photo, false, thumbSize) : undefined,
msrc: thumbSize ? utils.getPreviewUrl({ photo, msize: thumbSize }) : undefined,
}));
this.isOpen = true;
@ -759,7 +760,7 @@ export default defineComponent({
/** Get base data object */
getItemData(photo: IPhoto) {
let previewUrl = utils.getPreviewUrl(photo, false, 'screen');
let previewUrl = utils.getPreviewUrl({ photo, size: 'screen' });
const isvideo = photo.flag & this.c.FLAG_IS_VIDEO;
// Preview aren't animated

View File

@ -11,8 +11,48 @@ export function isMobile() {
return globalThis.windowInnerWidth <= 768;
}
/** Get preview URL from photo object */
export function getPreviewUrl(photo: IPhoto, square: boolean, size: number | [number, number] | 'screen') {
/** Preview generation options */
type PreviewOpts = {
/** Photo object to create preview for */
photo: IPhoto;
};
type PreviewOptsSize = PreviewOpts & {
/**
* Directly specify the size of the preview.
* If you already know the size of the photo, use msize instead,
* so that caching can be utilized best. A size of 256 is not allowed
* here size the thumbnails are not pre-generated.
*/
size: 512 | 1024 | 2048 | [number, number] | 'screen';
};
type PreviewOptsMsize = PreviewOpts & {
/**
* Size of minimum edge of the preview (recommended).
* This can only be used if the photo object has width and height.
*/
msize: 256 | 512 | 1024 | 2048;
};
type PreviewOptsSquare = PreviewOpts & {
/**
* Size of the square preview.
* Note that these will still be cached and requested with XImg multipreview.
*/
sqsize: 256 | 512 | 1024;
};
/**
* Get preview URL from photo object
*
* @param opts Preview options
*/
export function getPreviewUrl(opts: PreviewOptsSize | PreviewOptsMsize | PreviewOptsSquare) {
// Destructure does not work with union types
let { photo, size, msize, sqsize } = opts as PreviewOptsSize & PreviewOptsMsize & PreviewOptsSquare;
// Square size is just size
const square = sqsize !== undefined;
if (square) size = sqsize as any;
// Native preview
if (photo.flag & constants.c.FLAG_IS_LOCAL) {
return API.Q(nativex.API.IMAGE_PREVIEW(photo.fileid), { c: photo.etag });
@ -25,8 +65,18 @@ export function getPreviewUrl(photo: IPhoto, square: boolean, size: number | [nu
size = [sw, sh];
}
// Base size conversion
if (msize !== undefined) {
if (photo.w && photo.h) {
size = (Math.floor((msize * Math.max(photo.w, photo.h)) / Math.min(photo.w, photo.h)) - 1) as any;
} else {
console.warn('Photo has no width or height but using msize');
size = msize === 256 ? 512 : msize;
}
}
// Convert to array
const [x, y] = typeof size === 'number' ? [size, size] : size;
const [x, y] = typeof size === 'number' ? [size, size] : size!;
return API.Q(API.IMAGE_PREVIEW(photo.fileid), {
c: photo.etag,