Use createImageBitmap as hailmary

This commit is contained in:
Surma
2018-07-19 14:37:36 +01:00
parent b7c223bc0d
commit 13ac3ed5b2
3 changed files with 27 additions and 36 deletions

View File

@@ -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 cant 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(`Cant decode files with mime type ${mimeType}`);
}
console.log(`Going with ${decoder.name}`);
return decoder.decode(file);
} }

View 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);

View File

@@ -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> {