mirror of
https://github.com/CaramelFur/Picsur.git
synced 2025-11-13 07:15:39 +01:00
Add frontend qoi rendering
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { fileTypeFromBuffer, FileTypeResult } from 'file-type';
|
||||
import { FullMime } from 'picsur-shared/dist/dto/mimes.dto';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { ParseMime } from 'picsur-shared/dist/util/parse-mime';
|
||||
import { ImageDBService } from '../../collections/imagedb/imagedb.service';
|
||||
import { MimesService } from '../../collections/imagedb/mimes.service';
|
||||
import { FullMime } from '../../models/dto/mimes.dto';
|
||||
import { EImageBackend } from '../../models/entities/image.entity';
|
||||
import { ImageProcessorService } from './imageprocessor.service';
|
||||
|
||||
@@ -15,7 +15,6 @@ import { ImageProcessorService } from './imageprocessor.service';
|
||||
export class ImageManagerService {
|
||||
constructor(
|
||||
private readonly imagesService: ImageDBService,
|
||||
private readonly mimesService: MimesService,
|
||||
private readonly processService: ImageProcessorService,
|
||||
) {}
|
||||
|
||||
@@ -35,9 +34,16 @@ export class ImageManagerService {
|
||||
image: Buffer,
|
||||
userid: string,
|
||||
): AsyncFailable<EImageBackend> {
|
||||
let startTime = Date.now();
|
||||
|
||||
console.log('Uploading image');
|
||||
|
||||
const fullMime = await this.getFullMimeFromBuffer(image);
|
||||
if (HasFailed(fullMime)) return fullMime;
|
||||
|
||||
console.log('Got full mime after ' + (Date.now() - startTime) + 'ms');
|
||||
startTime = Date.now();
|
||||
|
||||
const processedImage = await this.processService.process(
|
||||
image,
|
||||
fullMime,
|
||||
@@ -45,20 +51,26 @@ export class ImageManagerService {
|
||||
);
|
||||
if (HasFailed(processedImage)) return processedImage;
|
||||
|
||||
console.log('Processed image after ' + (Date.now() - startTime) + 'ms');
|
||||
startTime = Date.now();
|
||||
|
||||
const imageEntity = await this.imagesService.create(
|
||||
processedImage,
|
||||
fullMime.mime,
|
||||
);
|
||||
if (HasFailed(imageEntity)) return imageEntity;
|
||||
|
||||
console.log('Created image after ' + (Date.now() - startTime) + 'ms');
|
||||
|
||||
return imageEntity;
|
||||
}
|
||||
|
||||
private async getFullMimeFromBuffer(image: Buffer): AsyncFailable<FullMime> {
|
||||
const mime: FileTypeResult | undefined = await fileTypeFromBuffer(image);
|
||||
const fullMime = await this.mimesService.getFullMime(
|
||||
mime?.mime ?? 'extra/discard',
|
||||
);
|
||||
|
||||
console.log(mime);
|
||||
|
||||
const fullMime = ParseMime(mime?.mime ?? 'extra/discard');
|
||||
return fullMime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as bmp from '@vingle/bmp-js';
|
||||
import icoToPng from 'ico-to-png';
|
||||
import { AsyncFailable, Fail } from 'picsur-shared/dist/types';
|
||||
import sharp from 'sharp';
|
||||
import { UsrPreferenceService } from '../../collections/preferencesdb/usrpreferencedb.service';
|
||||
import decodeico from 'decode-ico';
|
||||
import {
|
||||
FullMime,
|
||||
ImageMime,
|
||||
SupportedMimeCategory
|
||||
} from '../../models/dto/mimes.dto';
|
||||
} from 'picsur-shared/dist/dto/mimes.dto';
|
||||
import { AsyncFailable, Fail } from 'picsur-shared/dist/types';
|
||||
import { QOIColorSpace, QOIencode } from 'qoi-img';
|
||||
import sharp from 'sharp';
|
||||
import { UsrPreferenceService } from '../../collections/preferencesdb/usrpreferencedb.service';
|
||||
|
||||
@Injectable()
|
||||
export class ImageProcessorService {
|
||||
private readonly PngOptions = {
|
||||
compressionLevel: 9,
|
||||
effort: 10,
|
||||
};
|
||||
|
||||
constructor(private readonly userPref: UsrPreferenceService) {}
|
||||
|
||||
public async process(
|
||||
@@ -48,22 +44,38 @@ export class ImageProcessorService {
|
||||
mime: FullMime,
|
||||
options: {},
|
||||
): AsyncFailable<Buffer> {
|
||||
let processedImage = image;
|
||||
let sharpImage: sharp.Sharp;
|
||||
|
||||
if (mime.mime === ImageMime.ICO) {
|
||||
processedImage = await icoToPng(processedImage, 512);
|
||||
sharpImage = this.icoSharp(image);
|
||||
} else if (mime.mime === ImageMime.BMP) {
|
||||
processedImage = await this.bmpSharp(processedImage)
|
||||
.png(this.PngOptions)
|
||||
.toBuffer();
|
||||
sharpImage = this.bmpSharp(image);
|
||||
} else {
|
||||
processedImage = await sharp(processedImage)
|
||||
.png(this.PngOptions)
|
||||
.toBuffer();
|
||||
sharpImage = sharp(image);
|
||||
}
|
||||
mime.mime = ImageMime.PNG;
|
||||
|
||||
return processedImage;
|
||||
sharpImage = sharpImage.toColorspace('srgb');
|
||||
|
||||
const metadata = await sharpImage.metadata();
|
||||
const pixels = await sharpImage.raw().toBuffer();
|
||||
|
||||
if (
|
||||
metadata.hasAlpha === undefined ||
|
||||
metadata.width === undefined ||
|
||||
metadata.height === undefined
|
||||
)
|
||||
return Fail('Invalid image');
|
||||
|
||||
// Png can be more efficient than QOI, but its just sooooooo slow
|
||||
const qoiImage = QOIencode(pixels, {
|
||||
channels: metadata.hasAlpha ? 4 : 3,
|
||||
colorSpace: QOIColorSpace.SRGB,
|
||||
height: metadata.height,
|
||||
width: metadata.width,
|
||||
});
|
||||
|
||||
return qoiImage;
|
||||
}
|
||||
|
||||
private async processAnimation(
|
||||
@@ -85,4 +97,18 @@ export class ImageProcessorService {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private icoSharp(image: Buffer) {
|
||||
const result = decodeico(image);
|
||||
// Get biggest image
|
||||
const best = result.sort((a, b) => b.width - a.width)[0];
|
||||
|
||||
return sharp(best.data, {
|
||||
raw: {
|
||||
width: best.width,
|
||||
height: best.height,
|
||||
channels: 4,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user