2022-04-15 12:52:53 +02:00
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
|
import {
|
|
|
|
|
FullMime,
|
|
|
|
|
ImageMime,
|
2022-06-05 12:20:16 +02:00
|
|
|
SupportedMimeCategory,
|
2022-04-16 16:35:28 +02:00
|
|
|
} from 'picsur-shared/dist/dto/mimes.dto';
|
|
|
|
|
import { AsyncFailable, Fail } from 'picsur-shared/dist/types';
|
2022-04-25 13:14:44 +02:00
|
|
|
import { QOIColorSpace, QOIencode } from 'qoi-img';
|
|
|
|
|
import { ImageResult } from './imageresult';
|
|
|
|
|
import { UniversalSharp } from './universal-sharp';
|
2022-04-21 16:53:40 +02:00
|
|
|
|
2022-04-15 12:52:53 +02:00
|
|
|
@Injectable()
|
|
|
|
|
export class ImageProcessorService {
|
|
|
|
|
public async process(
|
|
|
|
|
image: Buffer,
|
|
|
|
|
mime: FullMime,
|
2022-04-25 13:14:44 +02:00
|
|
|
): AsyncFailable<ImageResult> {
|
2022-04-15 12:52:53 +02:00
|
|
|
if (mime.type === SupportedMimeCategory.Image) {
|
2022-04-25 13:14:44 +02:00
|
|
|
return await this.processStill(image, mime);
|
2022-04-15 12:52:53 +02:00
|
|
|
} else if (mime.type === SupportedMimeCategory.Animation) {
|
2022-04-25 13:14:44 +02:00
|
|
|
return await this.processAnimation(image, mime);
|
2022-04-15 12:52:53 +02:00
|
|
|
} else {
|
|
|
|
|
return Fail('Unsupported mime type');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async processStill(
|
|
|
|
|
image: Buffer,
|
|
|
|
|
mime: FullMime,
|
2022-04-25 13:14:44 +02:00
|
|
|
): AsyncFailable<ImageResult> {
|
2022-04-21 16:53:40 +02:00
|
|
|
let processedMime = mime.mime;
|
2022-04-15 12:52:53 +02:00
|
|
|
|
2022-04-25 13:14:44 +02:00
|
|
|
let sharpImage = UniversalSharp(image, mime);
|
2022-04-21 16:53:40 +02:00
|
|
|
processedMime = ImageMime.QOI;
|
2022-04-15 12:52:53 +02:00
|
|
|
|
2022-04-16 16:35:28 +02:00
|
|
|
sharpImage = sharpImage.toColorspace('srgb');
|
|
|
|
|
|
2022-05-01 23:29:56 +02:00
|
|
|
const processedImage = await sharpImage.raw().toBuffer({
|
|
|
|
|
resolveWithObject: true,
|
|
|
|
|
});
|
2022-04-16 16:35:28 +02:00
|
|
|
|
|
|
|
|
if (
|
2022-05-01 23:29:56 +02:00
|
|
|
processedImage.info.width >= 32768 ||
|
|
|
|
|
processedImage.info.height >= 32768
|
|
|
|
|
) {
|
2022-04-21 16:53:40 +02:00
|
|
|
return Fail('Image too large');
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-16 16:35:28 +02:00
|
|
|
// Png can be more efficient than QOI, but its just sooooooo slow
|
2022-05-01 23:29:56 +02:00
|
|
|
const qoiImage = QOIencode(processedImage.data, {
|
|
|
|
|
channels: processedImage.info.channels,
|
2022-04-25 13:14:44 +02:00
|
|
|
colorspace: QOIColorSpace.SRGB,
|
2022-05-01 23:29:56 +02:00
|
|
|
height: processedImage.info.height,
|
|
|
|
|
width: processedImage.info.width,
|
2022-04-16 16:35:28 +02:00
|
|
|
});
|
|
|
|
|
|
2022-04-21 16:53:40 +02:00
|
|
|
return {
|
|
|
|
|
image: qoiImage,
|
|
|
|
|
mime: processedMime,
|
|
|
|
|
};
|
2022-04-15 12:52:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async processAnimation(
|
|
|
|
|
image: Buffer,
|
|
|
|
|
mime: FullMime,
|
2022-04-25 13:14:44 +02:00
|
|
|
): AsyncFailable<ImageResult> {
|
2022-04-15 12:52:53 +02:00
|
|
|
// Apng and gif are stored as is for now
|
2022-04-21 16:53:40 +02:00
|
|
|
return {
|
|
|
|
|
image: image,
|
|
|
|
|
mime: mime.mime,
|
|
|
|
|
};
|
2022-04-15 12:52:53 +02:00
|
|
|
}
|
|
|
|
|
}
|