Source: resources/ImageTexture.js

import { Utils } from '../common/Utils'
import { BaseImageTexture } from './BaseImageTexture';
import { ImageWrapType, ImageMappingType } from '../const';
import { CubeTexture } from './CubeTexture';

/**
 * @class ImageTexture
 * The image texture resource.
 * @memberof THING
 * @extends THING.BaseImageTexture
 * @public
 */
class ImageTexture extends BaseImageTexture {

	/**
	 * The pixel buffer with size.
	 * @typedef {Object} PixelBuffer
	 * @property {Object} data The pixel buffer data.
	 * @property {Number} width The buffer width.
	 * @property {Number} height The buffer height.
	 */

	/**
	 * The pixel buffer with size.
	 * @typedef {Object} CanvasResource
	 * @property {HTMLElement} resource The pixel buffer data.
	 */

	/**
	 * The image texture that load image resource and use by style.
	 * @param {Object|PixelBuffer|CanvasResource} param The initial parameters.
	 * @example
	 * // Create image texture from pixel buffer with size
	 * let imageTexture = new THING.ImageTexture({
	 * 	data: pixelBuffer,
	 * 	width: 128,
	 * 	height: 128
	 * });
	 *
	 * // Create image texture from canvas
	 * let imageTexture = new THING.ImageTexture({
	 * 	resource: canvas
	 * });
	 */
	constructor(param = {}) {
		// Check whether it is cube texture.
		if (Utils.isArray(param) || Utils.isArray(param['url'])) {
			console.warn('Please create cube texture with THING.CubeTexture instead of THING.ImageTexture');
			return new CubeTexture(param);
		}

		super(param);
	}

	// #region Private

	/**
	 * Load texture resource.
	 * @param {Object} param The texture param.
	 * @private
	 */
	onLoad(param) {
		// Parse arguments
		if (Utils.isString(param)) {
			param = { url: param };
		}
		else if (param.width !== undefined && param.height !== undefined) {
			param = { resource: param };
		}

		let url = param['url'];

		// Load from data(could be compressed image data)
		if (param['type'] && param['data']) {
			// We can keep url as key even though it come from data
			this._url = url;

			this._loadFromData(param);
		}
		// Load from url
		else if (url) {
			this._loadFromURL(param);
		}
		// Start to load from resource by data(pixels), width and height
		else {
			let wrapType = param['wrapType'];
			if (wrapType) {
				this.wrapType = wrapType;
			}
			else {
				this.wrapTypeS = Utils.parseValue(param['wrapTypeS'], this.wrapTypeS);
				this.wrapTypeT = Utils.parseValue(param['wrapTypeT'], this.wrapTypeT);
			}

			this.mappingType = Utils.parseValue(param['mappingType'], this.mappingType);
			this.flipY = Utils.parseValue(param['flipY'], this.flipY);

			// Save image resource directly
			let data = param['data'];
			if (data) {
				let width = param['width'];
				let height = param['height'];

				this._image = {
					width,
					height,
					data,
				};
			}
			else {
				this._image = Utils.parseValue(param['resource'], null);
			}

			// Only set loaded flag when image resource is ready
			if (this._image) {
				this.onNotifyComplete();
			}
		}
	}

	_loadFromURL(param) {
		let onLoad = Utils.parseValue(param['onLoad'], null);

		this._url = param['url'];
		this._updateUrl();

		// Set options
		if (param['wrapType']) {
			this.wrapType = param['wrapType'];
		}

		this.mappingType = Utils.parseValue(param['mappingType'], ImageMappingType.UV);
		this.flipY = Utils.parseValue(param['flipY'], true);

		Utils.loadImageFile(this._url,
			// Load
			(image) => {
				this._image = image;

				this.onFixupFromImage(image);

				if (onLoad) {
					onLoad({ image: this });
				}

				this.onNotifyComplete();
			},
			// Progress
			(ev) => {

			},
			// Error
			(ev) => {
				this.onTriggerErrorEvent(ev);
			},
			{
				flipY: this.flipY,
				premultiplyAlpha: this.premultiplyAlpha,
				generateMipmaps: this.generateMipmaps,
				extensions: Utils.getCurrentApp().view.getExtensions()
			}
		);
	}

	_loadFromData(param) {
		let { type, data } = param

		let onLoad = Utils.parseValue(param['onLoad'], null);

		this.wrapType = Utils.parseValue(param['wrapType'], ImageWrapType.Repeat);
		this.mappingType = Utils.parseValue(param['mappingType'], ImageMappingType.UV);
		this.flipY = Utils.parseValue(param['flipY'], true);

		Utils.loadImageFileFromData(type, data,
			// Load
			(image) => {
				this._image = image;

				this.onFixupFromImage(image);

				if (onLoad) {
					onLoad({ image: this });
				}

				this.onNotifyComplete();
			},
			// Progress
			(ev) => {

			},
			// Error
			(ev) => {
				Utils.error(ev);
			},
			{
				extensions: Utils.getCurrentApp().view.getExtensions()
			}
		);
	}

	// #endregion

	// #region Overrides

	onDispose() {
		this._image = null;

		super.onDispose();
	}

	onCreateTextureResource() {
		// Some features do not support in compressed texture
		if (this._isCompressed(this._url)) {
			this.flipY = false;
			this.generateMipmaps = false;
		}

		let image = this._image;
		if (image) {
			// Image had been loaded
			return Utils.createObject('ImageTextureResource', { data: image, external: this.external });
		}
		else {
			// Image is still loading, we would replace it when it finished
			let textureResource = Utils.createObject('ImageTextureResource', { textureType: 'HTMLElement', external: this.external });

			this.waitForComplete().then((image) => {
				// Replace texture image
				textureResource.setImage(image.resource);
			}).catch(ev => {
				this.onTriggerErrorEvent(ev);
			});

			return textureResource;
		}
	}

	// #endregion


	// #region Accessors

	/**
	 * Get source url or base64 data.
	 * @type {String}
	 * @public
	 */
	get src() {
		let image = this._image;
		if (image) {
			if (image.src) {
				return image.src;
			}
		}

		return this._url;
	}

	/**
	 * Set the dirty flag.
	 * @type {Boolean}
	 * @private
	 */
	set dirty(value) {
		let textureResource = this.textureResource;
		if (!textureResource) {
			return;
		}

		textureResource.refresh();
	}

	/**
	 * Get width in pixel.
	 * @type {Number}
	 * @public
	 */
	get width() {
		let resource = this._image;
		if (!resource) {
			return 0;
		}

		if (resource.width) {
			return resource.width;
		}
		else if (resource.complete !== undefined) {
			return resource.width;
		}
		else if (Utils.isArray(resource)) {
			for (let i = 0; i < resource.length; i++) {
				if (resource[i].complete) {
					return resource[i].width;
				}
			}
		}

		return 0;
	}

	/**
	 * Get height in pixel.
	 * @type {Number}
	 * @public
	 */
	get height() {
		let resource = this._image;
		if (!resource) {
			return 0;
		}

		if (resource.height) {
			return resource.height;
		}
		else if (resource.complete !== undefined) {
			return resource.height;
		}
		else if (Utils.isArray(resource)) {
			for (let i = 0; i < resource.length; i++) {
				if (resource[i].complete) {
					return resource[i].height;
				}
			}
		}

		return 0;
	}

	/**
	 * Get/Set resource.
	 * @type {*}
	 * @private
	 */
	get resource() {
		return this._image;
	}
	set resource(value) {
		this._image = value;

		if (this.textureResource) {
			this.textureResource.setImage(value);
		}

		this.onNotifyComplete();
	}

	/**
	 * Enable/Disable flipY.
	 * @type {Boolean}
	 * @public
	 */
	get flipY() {
		// Compressed texture do not support flipY
		if (this._isCompressed(this._url)) {
			return false;
		}

		return super.flipY;
	}
	set flipY(value) {
		// Compressed texture do not support flipY
		if (value && this._isCompressed(this._url)) {
			return;
		}

		super.flipY = value;
	}

	/**
	 * Enable/Disable generate mipmaps.
	 * The minFilterType property will auto set to THING.ImageFilterType.LinearMipmapLinearFilter when generate mipmaps is enabled.
	 * @type {Boolean}
	 * @public
	 */
	get generateMipmaps() {
		// Compressed texture do not support generateMipmaps
		if (this._isCompressed(this._url)) {
			return false;
		}

		return super.generateMipmaps;
	}
	set generateMipmaps(value) {
		// Compressed texture do not support generateMipmaps
		if (value && this._isCompressed(this._url)) {
			return;
		}

		super.generateMipmaps = value;
	}

	// #endregion

	/**
	 * Check class type.
	 * @type {Boolean}
	 * @example
	 * 	if (texture.isImageTexture) {
	 * 		console.log(`It's image texture`);
	 * 	}
	 * @public
	 */
	get isImageTexture() {
		return true;
	}

}

export { ImageTexture }