import { Utils } from '../common/Utils'
import { BaseImageTexture } from './BaseImageTexture';
import { ImageWrapType, ImageMappingType } from '../const';
import { CubeTexture } from './CubeTexture';
/**
* ImageTexture - 图像纹理
*
* 用于加载图像资源并在样式中使用,支持从 URL、像素缓冲区或 Canvas 创建。
*
* @class ImageTexture
* @memberof THING
* @extends THING.BaseImageTexture
* @public
*
* @example
const texture = new THING.ImageTexture({
url: 'https://example.com/texture.png',
});
*/
class ImageTexture extends BaseImageTexture {
/**
* 数据类型的纹理
* @typedef {object} PixelBuffer
* @property {*} data The pixel buffer data. 纹理数据(一般都是TypedArray)
* @property {number} width The buffer width. 纹理宽
* @property {number} height The buffer height. 纹理高
*/
/**
* Canvas类型的纹理
* @typedef {object} CanvasResource
* @property {HTMLCanvasElement} resource The pixel buffer data. Canvas对象
*/
/**
* 图像纹理,用于加载图像资源并在样式中使用。
* @param {object|PixelBuffer|CanvasResource} param 初始化参数,支持 url、数据类型的纹理 Buffer 和 Canvas 类型的纹理 Buffer
* @param {string} param.url 贴图资源的路径
* @constructor
* @public
*/
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);
this.onTriggerErrorEvent(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, scene: this.app.scene.context, view: this.app.view });
}
else {
// Image is still loading, we would replace it when it finished
let textureResource = Utils.createObject('ImageTextureResource', { textureType: 'HTMLElement', external: this.external, scene: this.app.scene.context, view: this.app.view });
this.waitForComplete().then((image) => {
// Replace texture image
textureResource.setImage(image.resource);
}).catch(ev => {
this.onTriggerErrorEvent(ev);
});
return textureResource;
}
}
// #endregion
// #region Accessors
/**
* 贴图的 url 或者 base64 字符串
* @type {string}
* @public
* @readonly
*/
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();
}
/**
* 纹理的宽度(像素)
* @type {number}
* @public
* @readonly
*/
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;
}
/**
* 纹理的高度(像素)
* @type {number}
* @public
* @readonly
*/
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;
}
/**
* 设置/获取 纹理资源
* @type {*}
* @private
*/
get resource() {
return this._image;
}
set resource(value) {
this._image = value;
if (this.textureResource) {
this.textureResource.setImage(value);
}
this.onNotifyComplete();
}
/**
* 开启/禁用 y 轴翻转(y 轴镜像)
* @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;
}
/**
* 是否生成 mipMap。如果开启,纹理的 minFilterType 会被自动改成 LinearMipmapLinear
* @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
/**
* 是否是 ImageTexture
* @type {boolean}
*/
get isImageTexture() {
return true;
}
}
export { ImageTexture }