UNPKG

9.39 kBJavaScriptView Raw
1import { Rectangle, Point } from '@pixi/math';
2import { settings } from '@pixi/settings';
3import { EventEmitter, TextureCache, uid, getResolutionOfUrl } from '@pixi/utils';
4import { ImageResource } from './resources/ImageResource.mjs';
5import { BaseTexture } from './BaseTexture.mjs';
6import { TextureUvs } from './TextureUvs.mjs';
7
8const DEFAULT_UVS = new TextureUvs();
9function removeAllHandlers(tex) {
10 tex.destroy = function _emptyDestroy() {
11 };
12 tex.on = function _emptyOn() {
13 };
14 tex.once = function _emptyOnce() {
15 };
16 tex.emit = function _emptyEmit() {
17 };
18}
19class Texture extends EventEmitter {
20 constructor(baseTexture, frame, orig, trim, rotate, anchor) {
21 super();
22 this.noFrame = false;
23 if (!frame) {
24 this.noFrame = true;
25 frame = new Rectangle(0, 0, 1, 1);
26 }
27 if (baseTexture instanceof Texture) {
28 baseTexture = baseTexture.baseTexture;
29 }
30 this.baseTexture = baseTexture;
31 this._frame = frame;
32 this.trim = trim;
33 this.valid = false;
34 this._uvs = DEFAULT_UVS;
35 this.uvMatrix = null;
36 this.orig = orig || frame;
37 this._rotate = Number(rotate || 0);
38 if (rotate === true) {
39 this._rotate = 2;
40 } else if (this._rotate % 2 !== 0) {
41 throw new Error("attempt to use diamond-shaped UVs. If you are sure, set rotation manually");
42 }
43 this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0);
44 this._updateID = 0;
45 this.textureCacheIds = [];
46 if (!baseTexture.valid) {
47 baseTexture.once("loaded", this.onBaseTextureUpdated, this);
48 } else if (this.noFrame) {
49 if (baseTexture.valid) {
50 this.onBaseTextureUpdated(baseTexture);
51 }
52 } else {
53 this.frame = frame;
54 }
55 if (this.noFrame) {
56 baseTexture.on("update", this.onBaseTextureUpdated, this);
57 }
58 }
59 update() {
60 if (this.baseTexture.resource) {
61 this.baseTexture.resource.update();
62 }
63 }
64 onBaseTextureUpdated(baseTexture) {
65 if (this.noFrame) {
66 if (!this.baseTexture.valid) {
67 return;
68 }
69 this._frame.width = baseTexture.width;
70 this._frame.height = baseTexture.height;
71 this.valid = true;
72 this.updateUvs();
73 } else {
74 this.frame = this._frame;
75 }
76 this.emit("update", this);
77 }
78 destroy(destroyBase) {
79 if (this.baseTexture) {
80 if (destroyBase) {
81 const { resource } = this.baseTexture;
82 if (resource?.url && TextureCache[resource.url]) {
83 Texture.removeFromCache(resource.url);
84 }
85 this.baseTexture.destroy();
86 }
87 this.baseTexture.off("loaded", this.onBaseTextureUpdated, this);
88 this.baseTexture.off("update", this.onBaseTextureUpdated, this);
89 this.baseTexture = null;
90 }
91 this._frame = null;
92 this._uvs = null;
93 this.trim = null;
94 this.orig = null;
95 this.valid = false;
96 Texture.removeFromCache(this);
97 this.textureCacheIds = null;
98 }
99 clone() {
100 const clonedFrame = this._frame.clone();
101 const clonedOrig = this._frame === this.orig ? clonedFrame : this.orig.clone();
102 const clonedTexture = new Texture(this.baseTexture, !this.noFrame && clonedFrame, clonedOrig, this.trim?.clone(), this.rotate, this.defaultAnchor);
103 if (this.noFrame) {
104 clonedTexture._frame = clonedFrame;
105 }
106 return clonedTexture;
107 }
108 updateUvs() {
109 if (this._uvs === DEFAULT_UVS) {
110 this._uvs = new TextureUvs();
111 }
112 this._uvs.set(this._frame, this.baseTexture, this.rotate);
113 this._updateID++;
114 }
115 static from(source, options = {}, strict = settings.STRICT_TEXTURE_CACHE) {
116 const isFrame = typeof source === "string";
117 let cacheId = null;
118 if (isFrame) {
119 cacheId = source;
120 } else if (source instanceof BaseTexture) {
121 if (!source.cacheId) {
122 const prefix = options?.pixiIdPrefix || "pixiid";
123 source.cacheId = `${prefix}-${uid()}`;
124 BaseTexture.addToCache(source, source.cacheId);
125 }
126 cacheId = source.cacheId;
127 } else {
128 if (!source._pixiId) {
129 const prefix = options?.pixiIdPrefix || "pixiid";
130 source._pixiId = `${prefix}_${uid()}`;
131 }
132 cacheId = source._pixiId;
133 }
134 let texture = TextureCache[cacheId];
135 if (isFrame && strict && !texture) {
136 throw new Error(`The cacheId "${cacheId}" does not exist in TextureCache.`);
137 }
138 if (!texture && !(source instanceof BaseTexture)) {
139 if (!options.resolution) {
140 options.resolution = getResolutionOfUrl(source);
141 }
142 texture = new Texture(new BaseTexture(source, options));
143 texture.baseTexture.cacheId = cacheId;
144 BaseTexture.addToCache(texture.baseTexture, cacheId);
145 Texture.addToCache(texture, cacheId);
146 } else if (!texture && source instanceof BaseTexture) {
147 texture = new Texture(source);
148 Texture.addToCache(texture, cacheId);
149 }
150 return texture;
151 }
152 static fromURL(url, options) {
153 const resourceOptions = Object.assign({ autoLoad: false }, options?.resourceOptions);
154 const texture = Texture.from(url, Object.assign({ resourceOptions }, options), false);
155 const resource = texture.baseTexture.resource;
156 if (texture.baseTexture.valid) {
157 return Promise.resolve(texture);
158 }
159 return resource.load().then(() => Promise.resolve(texture));
160 }
161 static fromBuffer(buffer, width, height, options) {
162 return new Texture(BaseTexture.fromBuffer(buffer, width, height, options));
163 }
164 static fromLoader(source, imageUrl, name, options) {
165 const baseTexture = new BaseTexture(source, Object.assign({
166 scaleMode: settings.SCALE_MODE,
167 resolution: getResolutionOfUrl(imageUrl)
168 }, options));
169 const { resource } = baseTexture;
170 if (resource instanceof ImageResource) {
171 resource.url = imageUrl;
172 }
173 const texture = new Texture(baseTexture);
174 if (!name) {
175 name = imageUrl;
176 }
177 BaseTexture.addToCache(texture.baseTexture, name);
178 Texture.addToCache(texture, name);
179 if (name !== imageUrl) {
180 BaseTexture.addToCache(texture.baseTexture, imageUrl);
181 Texture.addToCache(texture, imageUrl);
182 }
183 if (texture.baseTexture.valid) {
184 return Promise.resolve(texture);
185 }
186 return new Promise((resolve) => {
187 texture.baseTexture.once("loaded", () => resolve(texture));
188 });
189 }
190 static addToCache(texture, id) {
191 if (id) {
192 if (!texture.textureCacheIds.includes(id)) {
193 texture.textureCacheIds.push(id);
194 }
195 if (TextureCache[id]) {
196 console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
197 }
198 TextureCache[id] = texture;
199 }
200 }
201 static removeFromCache(texture) {
202 if (typeof texture === "string") {
203 const textureFromCache = TextureCache[texture];
204 if (textureFromCache) {
205 const index = textureFromCache.textureCacheIds.indexOf(texture);
206 if (index > -1) {
207 textureFromCache.textureCacheIds.splice(index, 1);
208 }
209 delete TextureCache[texture];
210 return textureFromCache;
211 }
212 } else if (texture?.textureCacheIds) {
213 for (let i = 0; i < texture.textureCacheIds.length; ++i) {
214 if (TextureCache[texture.textureCacheIds[i]] === texture) {
215 delete TextureCache[texture.textureCacheIds[i]];
216 }
217 }
218 texture.textureCacheIds.length = 0;
219 return texture;
220 }
221 return null;
222 }
223 get resolution() {
224 return this.baseTexture.resolution;
225 }
226 get frame() {
227 return this._frame;
228 }
229 set frame(frame) {
230 this._frame = frame;
231 this.noFrame = false;
232 const { x, y, width, height } = frame;
233 const xNotFit = x + width > this.baseTexture.width;
234 const yNotFit = y + height > this.baseTexture.height;
235 if (xNotFit || yNotFit) {
236 const relationship = xNotFit && yNotFit ? "and" : "or";
237 const errorX = `X: ${x} + ${width} = ${x + width} > ${this.baseTexture.width}`;
238 const errorY = `Y: ${y} + ${height} = ${y + height} > ${this.baseTexture.height}`;
239 throw new Error(`Texture Error: frame does not fit inside the base Texture dimensions: ${errorX} ${relationship} ${errorY}`);
240 }
241 this.valid = width && height && this.baseTexture.valid;
242 if (!this.trim && !this.rotate) {
243 this.orig = frame;
244 }
245 if (this.valid) {
246 this.updateUvs();
247 }
248 }
249 get rotate() {
250 return this._rotate;
251 }
252 set rotate(rotate) {
253 this._rotate = rotate;
254 if (this.valid) {
255 this.updateUvs();
256 }
257 }
258 get width() {
259 return this.orig.width;
260 }
261 get height() {
262 return this.orig.height;
263 }
264 castToBaseTexture() {
265 return this.baseTexture;
266 }
267 static get EMPTY() {
268 if (!Texture._EMPTY) {
269 Texture._EMPTY = new Texture(new BaseTexture());
270 removeAllHandlers(Texture._EMPTY);
271 removeAllHandlers(Texture._EMPTY.baseTexture);
272 }
273 return Texture._EMPTY;
274 }
275 static get WHITE() {
276 if (!Texture._WHITE) {
277 const canvas = settings.ADAPTER.createCanvas(16, 16);
278 const context = canvas.getContext("2d");
279 canvas.width = 16;
280 canvas.height = 16;
281 context.fillStyle = "white";
282 context.fillRect(0, 0, 16, 16);
283 Texture._WHITE = new Texture(BaseTexture.from(canvas));
284 removeAllHandlers(Texture._WHITE);
285 removeAllHandlers(Texture._WHITE.baseTexture);
286 }
287 return Texture._WHITE;
288 }
289}
290
291export { Texture };
292//# sourceMappingURL=Texture.mjs.map