mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-13 01:07:18 +00:00
Use createImageBitmap as hailmary
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
import * as wasmWebP from './webp/decoder';
|
import * as wasmWebp from './webp/decoder';
|
||||||
import * as browserWebP from './browser-webp/decoder';
|
import * as browserWebp from './webp/decoder';
|
||||||
|
|
||||||
import { createImageBitmapPolyfill, sniffMimeType } from '../lib/util';
|
import { createImageBitmapPolyfill, sniffMimeType } from '../lib/util';
|
||||||
|
|
||||||
export interface Decoder {
|
export interface Decoder {
|
||||||
name: string;
|
name: string;
|
||||||
decode(file: File): Promise<ImageBitmap>;
|
decode(blob: Blob): Promise<ImageBitmap>;
|
||||||
isSupported(): Promise<boolean>;
|
isSupported(): Promise<boolean>;
|
||||||
canHandleMimeType(mimeType: string): boolean;
|
canHandleMimeType(mimeType: string): boolean;
|
||||||
}
|
}
|
||||||
@@ -13,8 +13,8 @@ export interface Decoder {
|
|||||||
// We load all decoders and filter out the unsupported ones.
|
// We load all decoders and filter out the unsupported ones.
|
||||||
export const decodersPromise: Promise<Decoder[]> = Promise.all(
|
export const decodersPromise: Promise<Decoder[]> = Promise.all(
|
||||||
[
|
[
|
||||||
browserWebP,
|
browserWebp,
|
||||||
wasmWebP,
|
wasmWebp,
|
||||||
]
|
]
|
||||||
.map(async (decoder) => {
|
.map(async (decoder) => {
|
||||||
if (await decoder.isSupported()) {
|
if (await decoder.isSupported()) {
|
||||||
@@ -26,33 +26,22 @@ export const decodersPromise: Promise<Decoder[]> = Promise.all(
|
|||||||
// values here.
|
// values here.
|
||||||
).then(list => list.filter(item => !!item)) as any as Promise<Decoder[]>;
|
).then(list => list.filter(item => !!item)) as any as Promise<Decoder[]>;
|
||||||
|
|
||||||
export async function findDecoder(file: File): Promise<Decoder | undefined> {
|
export async function findDecodersByMimeType(mimeType: string): Promise<Decoder[]> {
|
||||||
const decoders = await decodersPromise;
|
const decoders = await decodersPromise;
|
||||||
const mimeType = await sniffMimeType(file);
|
return decoders.filter(decoder => decoder.canHandleMimeType(mimeType));
|
||||||
if (!mimeType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return decoders.find(decoder => decoder.canHandleMimeType(mimeType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nativelySupportedMimeTypes = [
|
export async function decodeImage(blob: Blob): Promise<ImageBitmap> {
|
||||||
'image/jpeg',
|
const mimeType = await sniffMimeType(blob);
|
||||||
'image/png',
|
const decoders = await findDecodersByMimeType(mimeType);
|
||||||
'image/gif',
|
if (decoders.length <= 0) {
|
||||||
];
|
// If we can’t find a decoder, hailmary with createImageBitmap
|
||||||
|
return createImageBitmapPolyfill(blob);
|
||||||
export async function decodeFile(file: File): Promise<ImageBitmap> {
|
|
||||||
const mimeType = await sniffMimeType(file);
|
|
||||||
if (!mimeType) {
|
|
||||||
throw new Error('Could not determine mime type');
|
|
||||||
}
|
}
|
||||||
if (nativelySupportedMimeTypes.includes(mimeType)) {
|
for (const decoder of decoders) {
|
||||||
return createImageBitmapPolyfill(file);
|
try {
|
||||||
|
return await decoder.decode(blob);
|
||||||
|
} catch { }
|
||||||
}
|
}
|
||||||
const decoder = await findDecoder(file);
|
throw new Error('No decoder could decode image');
|
||||||
if (!decoder) {
|
|
||||||
throw new Error(`Can’t decode files with mime type ${mimeType}`);
|
|
||||||
}
|
|
||||||
console.log(`Going with ${decoder.name}`);
|
|
||||||
return decoder.decode(file);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
encoderMap,
|
encoderMap,
|
||||||
} from '../../codecs/encoders';
|
} from '../../codecs/encoders';
|
||||||
|
|
||||||
import { decodeFile } from '../../codecs/decoders';
|
import { decodeImage } from '../../codecs/decoders';
|
||||||
|
|
||||||
interface SourceImage {
|
interface SourceImage {
|
||||||
file: File;
|
file: File;
|
||||||
@@ -180,7 +180,7 @@ export default class App extends Component<Props, State> {
|
|||||||
async updateFile(file: File) {
|
async updateFile(file: File) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
try {
|
try {
|
||||||
const bmp = await decodeFile(file);
|
const bmp = await decodeImage(file);
|
||||||
// compute the corresponding ImageData once since it only changes when the file changes:
|
// compute the corresponding ImageData once since it only changes when the file changes:
|
||||||
const data = await bitmapToImageData(bmp);
|
const data = await bitmapToImageData(bmp);
|
||||||
|
|
||||||
|
|||||||
@@ -128,16 +128,18 @@ const magicNumberToMimeType = new Map<RegExp, string>([
|
|||||||
[/^RIFF....WEBPVP8 /, 'image/webp'],
|
[/^RIFF....WEBPVP8 /, 'image/webp'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export async function sniffMimeType(blob: Blob): Promise<string | undefined> {
|
export async function sniffMimeType(blob: Blob): Promise<string | ''> {
|
||||||
const firstChunk = await blobToArrayBuffer(blob.slice(0, 1024));
|
const firstChunk = await blobToArrayBuffer(blob.slice(0, 16));
|
||||||
const firstChunkString = Array.from(
|
const firstChunkString =
|
||||||
new Uint8Array(firstChunk)).map(v => String.fromCodePoint(v),
|
Array.from(new Uint8Array(firstChunk))
|
||||||
).join('');
|
.map(v => String.fromCodePoint(v))
|
||||||
|
.join('');
|
||||||
for (const [detector, mimeType] of magicNumberToMimeType.entries()) {
|
for (const [detector, mimeType] of magicNumberToMimeType.entries()) {
|
||||||
if (detector.test(firstChunkString)) {
|
if (detector.test(firstChunkString)) {
|
||||||
return mimeType;
|
return mimeType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createImageBitmapPolyfill(blob: Blob): Promise<ImageBitmap> {
|
export function createImageBitmapPolyfill(blob: Blob): Promise<ImageBitmap> {
|
||||||
|
|||||||
Reference in New Issue
Block a user