@awayjs/stage
Version:
Stage for AwayJS
175 lines (133 loc) • 4.76 kB
text/typescript
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;
}
}