mirror of
https://github.com/CaramelFur/Picsur.git
synced 2026-06-25 03:10:22 +02:00
Add ability to tell image to shrink only
Useful for thumbnails, no need to enlarge a small image
This commit is contained in:
@@ -72,19 +72,22 @@ export class ImageConverterService {
|
||||
|
||||
// Do modifications
|
||||
if (options.height || options.width) {
|
||||
if (options.height && options.width) {
|
||||
if ((options.height && options.width)) {
|
||||
sharpWrapper.operation('resize', {
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
fit: 'fill',
|
||||
kernel: 'cubic',
|
||||
withoutEnlargement: options.shrinkonly,
|
||||
});
|
||||
} else {
|
||||
sharpWrapper.operation('resize', {
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
fit: 'contain',
|
||||
fit: 'inside',
|
||||
kernel: 'cubic',
|
||||
|
||||
withoutEnlargement: options.shrinkonly,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ImagesComponent implements OnInit {
|
||||
|
||||
getThumbnailUrl(image: EImage) {
|
||||
return (
|
||||
this.imageService.GetImageURL(image.id, ImageFileType.QOI) + '?height=480'
|
||||
this.imageService.GetImageURL(image.id, ImageFileType.QOI) + '?height=480&shrinkonly=yes'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,10 @@
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-sm-6 col-12">
|
||||
<mat-slide-toggle [(ngModel)]="shrinkonly">Shrink only</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-sm-6 col-12">
|
||||
<mat-slide-toggle [(ngModel)]="greyscale">Greyscale</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,7 @@ export class CustomizeDialogComponent implements OnInit {
|
||||
public rotate: number;
|
||||
public flipx: boolean;
|
||||
public flipy: boolean;
|
||||
public shrinkonly: boolean;
|
||||
public greyscale: boolean;
|
||||
public noalpha: boolean;
|
||||
public negative: boolean;
|
||||
@@ -64,6 +65,7 @@ export class CustomizeDialogComponent implements OnInit {
|
||||
quality: this.quality ?? undefined,
|
||||
flipx: this.flipx,
|
||||
flipy: this.flipy,
|
||||
shrinkonly: this.shrinkonly,
|
||||
greyscale: this.greyscale,
|
||||
noalpha: this.noalpha,
|
||||
negative: this.negative,
|
||||
|
||||
@@ -80,11 +80,13 @@ export class ViewComponent implements OnInit {
|
||||
if (HasFailed(metadata))
|
||||
return this.utilService.quitError(metadata.getReason());
|
||||
|
||||
// Get width of screen in pixels
|
||||
const width = window.innerWidth * window.devicePixelRatio;
|
||||
|
||||
// Populate fields with metadata
|
||||
this.previewLink = this.imageService.GetImageURL(
|
||||
this.id,
|
||||
metadata.fileTypes.master,
|
||||
);
|
||||
this.previewLink =
|
||||
this.imageService.GetImageURL(this.id, metadata.fileTypes.master) +
|
||||
(width > 1 ? `?width=${width}&shrinkonly=yes` : '');
|
||||
|
||||
this.hasOriginal = metadata.fileTypes.original !== undefined;
|
||||
|
||||
@@ -162,10 +164,7 @@ export class ViewComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
this.utilService.showSnackBar(
|
||||
'Image deleted',
|
||||
SnackBarType.Success,
|
||||
);
|
||||
this.utilService.showSnackBar('Image deleted', SnackBarType.Success);
|
||||
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,13 @@ import { ImageLinks } from 'picsur-shared/dist/dto/image-links.class';
|
||||
import { FileType2Ext } from 'picsur-shared/dist/dto/mimes.dto';
|
||||
import { EImage } from 'picsur-shared/dist/entities/image.entity';
|
||||
import { AsyncFailable } from 'picsur-shared/dist/types';
|
||||
import { Fail, FT, HasFailed, HasSuccess, Open } from 'picsur-shared/dist/types/failable';
|
||||
import {
|
||||
Fail,
|
||||
FT,
|
||||
HasFailed,
|
||||
HasSuccess,
|
||||
Open
|
||||
} from 'picsur-shared/dist/types/failable';
|
||||
import { ImageUploadRequest } from '../../models/dto/image-upload-request.dto';
|
||||
import { ApiService } from './api.service';
|
||||
import { UserService } from './user.service';
|
||||
@@ -107,7 +113,9 @@ export class ImageService {
|
||||
const baseURL = this.location.protocol + '//' + this.location.host;
|
||||
const extension = FileType2Ext(filetype ?? '');
|
||||
|
||||
return `${baseURL}/i/${image}${HasSuccess(extension) ? '.' + extension : ''}`;
|
||||
return `${baseURL}/i/${image}${
|
||||
HasSuccess(extension) ? '.' + extension : ''
|
||||
}`;
|
||||
}
|
||||
|
||||
public GetImageURLCustomized(
|
||||
@@ -129,6 +137,8 @@ export class ImageService {
|
||||
queryParams.push(`rotate=${options.rotate}`);
|
||||
if (options.flipx !== undefined) queryParams.push(`flipx=${options.flipx}`);
|
||||
if (options.flipy !== undefined) queryParams.push(`flipy=${options.flipy}`);
|
||||
if (options.shrinkonly !== undefined)
|
||||
queryParams.push(`shrinkonly=${options.shrinkonly}`);
|
||||
if (options.greyscale !== undefined)
|
||||
queryParams.push(`greyscale=${options.greyscale}`);
|
||||
if (options.noalpha !== undefined)
|
||||
|
||||
@@ -2,15 +2,15 @@ import { z } from 'zod';
|
||||
import { EImageSchema } from '../../entities/image.entity';
|
||||
import { EUserSchema } from '../../entities/user.entity';
|
||||
import { createZodDto } from '../../util/create-zod-dto';
|
||||
import { ParseBool } from '../../util/parse-simple';
|
||||
import { ParseBool, ParseInt } from '../../util/parse-simple';
|
||||
import { ImageEntryVariant } from '../image-entry-variant.enum';
|
||||
|
||||
export const ImageRequestParamsSchema = z
|
||||
.object({
|
||||
height: z.preprocess(Number, z.number().int().min(1).max(32767)),
|
||||
width: z.preprocess(Number, z.number().int().min(1).max(32767)),
|
||||
height: z.preprocess(ParseInt, z.number().int().min(1).max(32767)),
|
||||
width: z.preprocess(ParseInt, z.number().int().min(1).max(32767)),
|
||||
rotate: z.preprocess(
|
||||
Number,
|
||||
ParseInt,
|
||||
z.number().int().multipleOf(90).min(0).max(360),
|
||||
),
|
||||
flipx: z.preprocess(ParseBool, z.boolean()),
|
||||
@@ -18,7 +18,8 @@ export const ImageRequestParamsSchema = z
|
||||
greyscale: z.preprocess(ParseBool, z.boolean()),
|
||||
noalpha: z.preprocess(ParseBool, z.boolean()),
|
||||
negative: z.preprocess(ParseBool, z.boolean()),
|
||||
quality: z.preprocess(Number, z.number().int().min(1).max(100)),
|
||||
shrinkonly: z.preprocess(ParseBool, z.boolean()),
|
||||
quality: z.preprocess(ParseInt, z.number().int().min(1).max(100)),
|
||||
})
|
||||
.partial();
|
||||
|
||||
|
||||
@@ -14,13 +14,17 @@ export const ParseInt = <T extends number | null = null>(
|
||||
value: unknown,
|
||||
fallback?: T,
|
||||
): number | T => {
|
||||
if (typeof value === 'number') return value;
|
||||
if (typeof value === 'number') return Math.round(value);
|
||||
if (typeof value === 'boolean') return value ? 1 : 0;
|
||||
if (typeof value === 'string') {
|
||||
const parsed = parseInt(value);
|
||||
if (!isNaN(parsed)) return parsed;
|
||||
const parsed = Number(value);
|
||||
if (!isNaN(parsed)) return Math.round(parsed);
|
||||
}
|
||||
return fallback === undefined ? (null as T) : fallback;
|
||||
return fallback === undefined
|
||||
? (null as T)
|
||||
: fallback === null
|
||||
? fallback
|
||||
: Math.round(fallback);
|
||||
};
|
||||
|
||||
export const ParseString = <T extends string | null = null>(
|
||||
|
||||
Reference in New Issue
Block a user