Home Manual Reference Source

src/basic/S3Texture.js

import S3System from "./S3System.js";

/**
 * 3DCG用のテクスチャ(画像)情報を管理するクラス
 * 画像のセットや2の累乗化処理、ロード状況管理、破棄処理などを担当します。
 *
 * @class
 * @module S3
 */
export default class S3Texture {
	/**
	 * テクスチャを作成します。
	 * @param {S3System} s3system S3Systemインスタンス(画像ID発行・ダウンロード補助用)
	 * @param {string|ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} [data] 初期化時に与える画像やURL等(省略可)
	 */
	constructor(s3system, data) {
		/**
		 * システムインスタンス
		 * @type {S3System}
		 */
		this.sys = s3system;
		this._init();
		if (data !== undefined) {
			this.setImage(data);
		}
	}

	/**
	 * テクスチャ情報を初期化します。ロードフラグや画像情報をリセットします。
	 * @protect
	 */
	_init() {
		/**
		 * テクスチャのURLやID
		 * @type {?string}
		 */
		this.url = null;

		/**
		 * テクスチャ画像本体(ImageData, HTMLImageElement, HTMLCanvasElement, HTMLVideoElementなど)
		 * @type {?ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement}
		 */
		this.image = null;

		/**
		 * 画像がロード済みかどうか
		 * @type {boolean}
		 */
		this.is_loadimage = false;

		/**
		 * テクスチャが破棄されたかどうか
		 * @type {boolean}
		 */
		this.is_dispose = false;
	}

	/**
	 * テクスチャを破棄します。再利用は不可になります。
	 */
	dispose() {
		if (!this.is_dispose) {
			this.is_dispose = true;
		}
	}

	/**
	 * テクスチャ画像を設定します。
	 * - 画像が2の累乗でない場合は自動でリサイズします。
	 * - 文字列の場合はURLとして画像をダウンロードします。
	 * - 設定可能な形式: ImageData, HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, URL(string)
	 * @param {string|ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image 設定する画像データまたはURL文字列
	 */
	setImage(image) {
		if (image === null || this.is_dispose) {
			return;
		}
		if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement) {
			const original_width = image.width;
			const original_height = image.height;
			/**
			 * 指定された値以上の最小の2のべき乗の値を返します。
			 * 例えば、5を渡すと8を返し、8を渡すと8を返します。
			 * Math.log2 が使えない環境(例: IE)でも動作します。
			 *
			 * @param {number} x - 2のべき乗に切り上げたい対象の数値
			 * @returns {number} x以上の最小の2のべき乗の値
			 *
			 * @example
			 * ceil_power_of_2(5); // 8
			 * ceil_power_of_2(8); // 8
			 * ceil_power_of_2(15); // 16
			 */
			const ceil_power_of_2 = function (x) {
				// IE には Math.log2 がない
				const a = Math.log(x) / Math.log(2);
				if (a - Math.floor(a) < 1e-10) {
					return x;
				} else {
					return 1 << Math.ceil(a);
				}
			};
			const ceil_width = ceil_power_of_2(original_width);
			const ceil_height = ceil_power_of_2(original_height);
			if (original_width !== ceil_width || original_height !== ceil_height) {
				// 2の累乗ではない場合は、2の累乗のサイズに変換
				const ceil_image = document.createElement("canvas");
				ceil_image.width = ceil_width;
				ceil_image.height = ceil_height;
				ceil_image
					.getContext("2d")
					.drawImage(image, 0, 0, original_width, original_height, 0, 0, ceil_width, ceil_height);
				image = ceil_image;
			}
		}
		if (
			image instanceof ImageData ||
			image instanceof HTMLImageElement ||
			image instanceof HTMLCanvasElement ||
			image instanceof HTMLVideoElement
		) {
			if (this.url === null) {
				// 直接設定した場合はIDをURLとして設定する
				this.url = this.sys._createID();
			}
			this.image = image;
			this.is_loadimage = true;
			return;
		} else if (typeof image === "string") {
			this.url = image;
			const that = this;
			this.sys._download(
				this.url,
				/**
				 * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image
				 * @returns {void}
				 */
				function (image) {
					that.setImage(image);
				}
			);
			return;
		} else {
			console.log("not setImage");
			console.log(image);
		}
	}
}