UNPKG

@awayjs/stage

Version:
175 lines (133 loc) 4.76 kB
import { IAsset, URLLoaderDataFormat, ParserBase, ByteArray, URLRequest } from '@awayjs/core'; import { Image2D } from '../image/Image2D'; import { ExternalImage2D } from '../image/ExternalImage2D'; import { ImageUtils } from '../utils/ImageUtils'; import { DefaultStageFactory } from '../factories/DefaultStageFactory'; import { IImageFactory } from '../factories/IImageFactory'; import { Settings } from '../Settings'; /** * Image2DParser provides a "parser" for natively supported image types (jpg, png). While it simply loads bytes into * a loader object, it wraps it in a BitmapDataResource so resource management can happen consistently without * exception cases. */ type TImage = HTMLImageElement | ImageBitmap; function getImageFromData (blob: Blob, callback: (arg: TImage) => void): TImage { // self is window for main ui or self for worker, need use it const supportImageBitmap = Settings.ENABLE_PARSER_NATIVE_BITMAP && ('createImageBitmap' in self); if (supportImageBitmap) { self.createImageBitmap(blob).then(callback); return null; } const url = URL.createObjectURL(blob); const image = new Image(); image.src = url; if (!image.naturalWidth) { image.onload = (event) => { callback(image); URL.revokeObjectURL(url); }; return null; } return image; } export class Image2DParser extends ParserBase { public static SUPPORTED_TYPES = ['jpg', 'jpeg', 'png', 'gif']; private _factory: IImageFactory; private _alphaChannel: Uint8Array; /** * Creates a new Image2DParser object. * @param uri The url or id of the data or file to be parsed. * @param extra The holder for extra contextual data that the parser might need. */ constructor(factory: IImageFactory = null, alphaChannel: Uint8Array = null) { super(URLLoaderDataFormat.BLOB); this._factory = factory || new DefaultStageFactory(); this._alphaChannel = alphaChannel; } /** * Indicates whether or not a given file extension is supported by the parser. * @param extension The file extension of a potential file to be parsed. * @return Whether or not the given file type is supported. */ public static supportsType(extension: string): boolean { extension = extension.toLowerCase().trim(); return this.SUPPORTED_TYPES.includes(extension); } /** * Tests whether a data block can be parsed by the parser. * @param data The data block to potentially be parsed. * @return Whether or not the given data is supported. */ public static supportsData(data: any): boolean { if (data instanceof HTMLImageElement) return true; if (!(data instanceof ByteArray)) return false; const ba: ByteArray = <ByteArray> data; ba.position = 0; if (ba.readUnsignedShort() == 0xd8ff) return true; // JPEG, maybe check for "JFIF" as well? ba.position = 0; if (ba.readShort() == 0x424D) return true; // BMP ba.position = 1; if (ba.readUTFBytes(3) == 'PNG') return true; ba.position = 0; if (ba.readUTFBytes(3) == 'GIF' && ba.readShort() == 0x3839 && ba.readByte() == 0x61) return true; ba.position = 0; if (ba.readUTFBytes(3) == 'ATF') return true; return false; } protected finalizeAsset(asset: IAsset, fileName: string): void { if (this._alphaChannel) (<Image2D>asset).alphaChannel = this._alphaChannel; super.finalizeAsset(asset, fileName); } /** * @inheritDoc */ protected proceedParsing(): void { let asset: Image2D; if (this.data instanceof HTMLImageElement) { //data is already an image this._finalizeAssetFromImage(this.data); } else if (this.data instanceof URLRequest) { //data is an external image asset = new ExternalImage2D(<URLRequest> this.data); this.finalizeAsset(<IAsset> asset, this.fileName); this._content = asset; } else { //check if data can be resolved to a blob let blob: Blob; if (this.data instanceof ByteArray) { // Parse a ByteArray const ba: ByteArray = this.data; ba.position = 0; blob = new Blob([this.data.arraybytes]); } else if (this.data instanceof ArrayBuffer) {// Parse an ArrayBuffer blob = new Blob([this.data]); } else if (this.data instanceof Blob) { blob = this.data; } else { throw 'Unknow data'; } const image = getImageFromData(blob, (image) => { this._finalizeAssetFromImage(image); //notify parser of async parsing completion this.finishParsing(); }); if (image) this._finalizeAssetFromImage(image); } //notify parser of sync parsing completion if (this._content) this.finishParsing(); } private _finalizeAssetFromImage(image: TImage): void { const asset = ImageUtils.imageToBitmapImage2D(image, this._factory, false); this.finalizeAsset(asset, this.fileName); this._content = asset; } }