UNPKG

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