| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833 |
- import BoundingRectangle from "../Core/BoundingRectangle.js";
- import Cartesian2 from "../Core/Cartesian2.js";
- import Check from "../Core/Check.js";
- import createGuid from "../Core/createGuid.js";
- import Frozen from "../Core/Frozen.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.js";
- import CesiumMath from "../Core/Math.js";
- import PixelFormat from "../Core/PixelFormat.js";
- import Resource from "../Core/Resource.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import TexturePacker from "../Core/TexturePacker.js";
- import Framebuffer from "./Framebuffer.js";
- import Texture from "./Texture.js";
-
- const defaultInitialDimensions = 16;
-
- /**
- * A TextureAtlas stores multiple images in one∂ texture and keeps
- * track of the texture coordinates for each image. A TextureAtlas is dynamic,
- * meaning new images can be added at any point in time.
- * Texture coordinates are subject to change if the texture atlas resizes, so it's
- * important to check {@link TextureAtlas#guid} before using old values.
- *
- * @alias TextureAtlas
- * @constructor
- *
- * @param {object} options Object with the following properties:
- * @param {PixelFormat} [options.pixelFormat=PixelFormat.RGBA] The pixel format of the texture.
- * @param {Sampler} [options.sampler=new Sampler()] Information about how to sample the texture.
- * @param {number} [options.borderWidthInPixels=1] The amount of spacing between adjacent images in pixels.
- * @param {Cartesian2} [options.initialSize=new Cartesian2(16.0, 16.0)] The initial side lengths of the texture.
- *
- * @exception {DeveloperError} borderWidthInPixels must be greater than or equal to zero.
- * @exception {DeveloperError} initialSize must be greater than zero.
- *
- * @private
- */
- function TextureAtlas(options) {
- options = options ?? Frozen.EMPTY_OBJECT;
- const borderWidthInPixels = options.borderWidthInPixels ?? 1.0;
- const initialSize =
- options.initialSize ??
- new Cartesian2(defaultInitialDimensions, defaultInitialDimensions);
-
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number.greaterThanOrEquals(
- "options.borderWidthInPixels",
- borderWidthInPixels,
- 0,
- );
- Check.typeOf.number.greaterThan("options.initialSize.x", initialSize.x, 0);
- Check.typeOf.number.greaterThan("options.initialSize.y", initialSize.y, 0);
- //>>includeEnd('debug');
-
- this._pixelFormat = options.pixelFormat ?? PixelFormat.RGBA;
- this._sampler = options.sampler;
- this._borderWidthInPixels = borderWidthInPixels;
- this._initialSize = initialSize;
-
- this._texturePacker = undefined;
- /** @type {BoundingRectangle[]} */
- this._rectangles = [];
- /** @type {Map<number, number>} */
- this._subRegions = new Map();
- this._guid = createGuid();
-
- this._imagesToAddQueue = [];
- /** @type {Map<string, number>} */
- this._indexById = new Map();
- /** @type {Map<string, Promise<number>>} */
- this._indexPromiseById = new Map();
- this._nextIndex = 0;
- }
-
- Object.defineProperties(TextureAtlas.prototype, {
- /**
- * The amount of spacing between adjacent images in pixels.
- * @memberof TextureAtlas.prototype
- * @type {number}
- * @readonly
- * @private
- */
- borderWidthInPixels: {
- get: function () {
- return this._borderWidthInPixels;
- },
- },
-
- /**
- * An array of {@link BoundingRectangle} pixel offset and dimensions for all the images in the texture atlas.
- * The x and y values of the rectangle correspond to the bottom-left corner of the texture coordinate.
- * If the index is a subregion of an existing image, thea and y values are specified as offsets relative to the parent.
- * The coordinates are in the order that the corresponding images were added to the atlas.
- * @memberof TextureAtlas.prototype
- * @type {BoundingRectangle[]}
- * @readonly
- * @private
- */
- rectangles: {
- get: function () {
- return this._rectangles;
- },
- },
-
- /**
- * The texture that all of the images are being written to. The value will be <code>undefined</code> until the first update.
- * @memberof TextureAtlas.prototype
- * @type {Texture|undefined}
- * @readonly
- * @private
- */
- texture: {
- get: function () {
- return this._texture;
- },
- },
-
- /**
- * The pixel format of the texture.
- * @memberof TextureAtlas.prototype
- * @type {PixelFormat}
- * @readonly
- * @private
- */
- pixelFormat: {
- get: function () {
- return this._pixelFormat;
- },
- },
-
- /**
- * The sampler to use when sampling this texture. If <code>undefined</code>, the default sampler is used.
- * @memberof TextureAtlas.prototype
- * @type {Sampler|undefined}
- * @readonly
- * @private
- */
- sampler: {
- get: function () {
- return this._sampler;
- },
- },
-
- /**
- * The number of images in the texture atlas. This value increases
- * every time addImage or addImageSubRegion is called.
- * Texture coordinates are subject to change if the texture atlas resizes, so it is
- * important to check {@link TextureAtlas#guid} before using old values.
- * @memberof TextureAtlas.prototype
- * @type {number}
- * @readonly
- * @private
- */
- numberOfImages: {
- get: function () {
- return this._nextIndex;
- },
- },
-
- /**
- * The atlas' globally unique identifier (GUID).
- * The GUID changes whenever the texture atlas is modified.
- * Classes that use a texture atlas should check if the GUID
- * has changed before processing the atlas data.
- * @memberof TextureAtlas.prototype
- * @type {string}
- * @readonly
- * @private
- */
- guid: {
- get: function () {
- return this._guid;
- },
- },
-
- /**
- * Returns the size in bytes of the texture.
- * @memberof TextureAtlas.prototype
- * @type {number}
- * @readonly
- * @private
- */
- sizeInBytes: {
- get: function () {
- if (!defined(this._texture)) {
- return 0;
- }
-
- return this._texture.sizeInBytes;
- },
- },
- });
-
- /**
- * Get the texture coordinates for reading the associated image in shaders.
- * @param {number} index The index of the image region.
- * @param {BoundingRectangle} [result] The object into which to store the result.
- * @return {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided.
- * @private
- * @example
- * const index = await atlas.addImage("myImage", image);
- * const rectangle = atlas.computeTextureCoordinates(index);
- * BoundingRectangle.pack(rectangle, bufferView);
- */
- TextureAtlas.prototype.computeTextureCoordinates = function (index, result) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number.greaterThanOrEquals("index", index, 0);
- //>>includeEnd('debug');
-
- const texture = this._texture;
- const rectangle = this._rectangles[index];
-
- if (!defined(result)) {
- result = new BoundingRectangle();
- }
-
- if (!defined(rectangle)) {
- result.x = 0;
- result.y = 0;
- result.width = 0;
- result.height = 0;
-
- return result;
- }
-
- const atlasWidth = texture.width;
- const atlasHeight = texture.height;
-
- const width = rectangle.width;
- const height = rectangle.height;
- let x = rectangle.x;
- let y = rectangle.y;
-
- const parentIndex = this._subRegions.get(index);
- if (defined(parentIndex)) {
- const parentRectangle = this._rectangles[parentIndex];
-
- x += parentRectangle.x;
- y += parentRectangle.y;
- }
-
- result.x = x / atlasWidth;
- result.y = y / atlasHeight;
- result.width = width / atlasWidth;
- result.height = height / atlasHeight;
-
- return result;
- };
-
- /**
- * Perform a WebGL texture copy for each existing image from its previous packed position to its new packed position in the new texture.
- * @param {Context} context The rendering context
- * @param {number} width The pixel width of the texture
- * @param {number} height The pixel height of the texture
- * @param {BoundingRectangle[]} rectangles The packed bounding rectangles for the reszied texture
- * @param {number} queueOffset Index of the last queued item that was successfully packed
- * @private
- */
- TextureAtlas.prototype._copyFromTexture = function (
- context,
- width,
- height,
- rectangles,
- ) {
- const pixelFormat = this._pixelFormat;
- const sampler = this._sampler;
- const newTexture = new Texture({
- context,
- height,
- width,
- pixelFormat,
- sampler,
- });
-
- const gl = context._gl;
- const target = newTexture._textureTarget;
-
- const oldTexture = this._texture;
- const framebuffer = new Framebuffer({
- context,
- colorTextures: [oldTexture],
- destroyAttachments: false,
- });
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(target, newTexture._texture);
-
- framebuffer._bind();
-
- // Copy any textures from the old atlas to its new position in the new atlas
- const oldRectangles = this.rectangles;
- const subRegions = this._subRegions;
- for (let index = 0; index < oldRectangles.length; ++index) {
- const rectangle = rectangles[index];
- const frameBufferOffset = oldRectangles[index];
-
- if (
- !defined(rectangle) ||
- !defined(frameBufferOffset) ||
- defined(subRegions.get(index)) // The rectangle corresponds to a subregion of a parent image
- ) {
- continue;
- }
-
- const { x, y, width, height } = rectangle;
- gl.copyTexSubImage2D(
- target,
- 0,
- x,
- y,
- frameBufferOffset.x,
- frameBufferOffset.y,
- width,
- height,
- );
- }
-
- gl.bindTexture(target, null);
- newTexture._initialized = true;
-
- framebuffer._unBind();
- framebuffer.destroy();
-
- return newTexture;
- };
-
- /**
- * Recreates the texture atlas texture with new dimensions and repacks images as needed.
- * @param {Context} context The rendering context
- * @param {number} [queueOffset = 0] Index of the last queued item that was successfully packed
- * @private
- */
- TextureAtlas.prototype._resize = function (context, queueOffset = 0) {
- const borderPadding = this._borderWidthInPixels;
- const oldRectangles = this._rectangles;
- const queue = this._imagesToAddQueue;
-
- const oldTexture = this._texture;
- let width = oldTexture.width;
- let height = oldTexture.height;
-
- // Get the rectangles (width and height) of the current set of images,
- // ignoring the subregions, which don't get packed
- const subRegions = this._subRegions;
- const toPack = oldRectangles
- .map((image, index) => {
- return new AddImageRequest({ index, image });
- })
- .filter(
- (request, index) =>
- defined(request.image) && !defined(subRegions.get(index)),
- );
-
- // Add the new set of images
- let maxWidth = 0;
- let maxHeight = 0;
- let areaQueued = 0;
- for (let i = queueOffset; i < queue.length; ++i) {
- const { width, height } = queue[i].image;
- maxWidth = Math.max(maxWidth, width);
- maxHeight = Math.max(maxHeight, height);
- areaQueued += width * height;
- toPack.push(queue[i]);
- }
-
- // At minimum, atlas must fit its largest input images. Texture coordinates are
- // compressed to 0–1 with 12-bit precision, so use power-of-two size to align pixels.
- width = CesiumMath.nextPowerOfTwo(Math.max(maxWidth, width));
- height = CesiumMath.nextPowerOfTwo(Math.max(maxHeight, height));
-
- // Iteratively double the smallest dimension until atlas area is (approximately) sufficient.
- while (areaQueued >= width * height) {
- if (width > height) {
- height *= 2;
- } else {
- width *= 2;
- }
- }
-
- toPack.sort(
- ({ image: imageA }, { image: imageB }) =>
- imageB.height * imageB.width - imageA.height * imageA.width,
- );
-
- const newRectangles = new Array(this._nextIndex);
- for (const index of this._subRegions.keys()) {
- // Subregions are specified relative to their parents,
- // so we can copy them directly
- if (defined(subRegions.get(index))) {
- newRectangles[index] = oldRectangles[index];
- }
- }
-
- let texturePacker,
- packed = false;
- while (!packed) {
- texturePacker = new TexturePacker({ height, width, borderPadding });
-
- let i;
- for (i = 0; i < toPack.length; ++i) {
- const { index, image } = toPack[i];
- if (!defined(image)) {
- continue;
- }
-
- const repackedNode = texturePacker.pack(index, image);
- if (!defined(repackedNode)) {
- // Could not fit everything into the new texture.
- // Scale texture size and try again
- if (width > height) {
- // Resize height
- height *= 2.0;
- } else {
- // Resize width
- width *= 2.0;
- }
-
- break;
- }
-
- newRectangles[index] = repackedNode.rectangle;
- }
-
- packed = i === toPack.length;
- }
-
- this._texturePacker = texturePacker;
- this._texture = this._copyFromTexture(context, width, height, newRectangles);
-
- oldTexture.destroy();
-
- this._rectangles = newRectangles;
- this._guid = createGuid();
- };
-
- /**
- * Return the index of the image region for the specified ID. If the image is already in the atlas, the existing index is returned. Otherwise, the result is undefined.
- * @param {string} id An identifier to detect whether the image already exists in the atlas.
- * @returns {number|undefined} The image index, or undefined if the image does not exist in the atlas.
- * @private
- */
- TextureAtlas.prototype.getImageIndex = function (id) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.string("id", id);
- //>>includeEnd('debug');
-
- return this._indexById.get(id);
- };
-
- /**
- * Copy image data into the underlying texture atlas.
- * @param {AddImageRequest} imageRequest The data needed to resolve the call to addImage in the queue
- * @private
- */
- TextureAtlas.prototype._copyImageToTexture = function ({
- index,
- image,
- resolve,
- reject,
- }) {
- const texture = this._texture;
- const rectangle = this._rectangles[index];
-
- try {
- texture.copyFrom({
- source: image,
- xOffset: rectangle.x,
- yOffset: rectangle.y,
- });
-
- if (defined(resolve)) {
- resolve(index);
- }
- } catch (e) {
- if (defined(reject)) {
- reject(e);
- return;
- }
- }
- };
-
- /**
- * Info needed to add a queued image to the texture atlas when update operatons are executed, typically at the end of a frame.
- * @constructor
- * @private
- * @param {object} options Object with the following properties:
- * @param {number} options.index An identifier
- * @param {TexturePacker.PackableObject} options.image An object, such as an <code>Image</code> with <code>width</code> and <code>height</code> properties in pixels
- * @param {function} [options.resolve] The promise resolver
- * @param {function} [options.reject] The promise rejecter
- */
- function AddImageRequest({ index, image, resolve, reject }) {
- this.index = index;
- this.image = image;
- this.resolve = resolve;
- this.reject = reject;
- this.rectangle = undefined;
- }
-
- /**
- * Adds an image to the queue for this frame.
- * The image will be copied to the texture at the end of the frame, resizing the texture if needed.
- *
- * @private
- * @param {number} index An identifier
- * @param {TexturePacker.PackableObject} image An object, such as an <code>Image</code> with <code>width</code> and <code>height</code> properties in pixels
- * @returns {Promise<number>} Promise which resolves to the image index once the image has been added, or rejects if there was an error. The promise resolves to <code>-1</code> if the texture atlas is destoyed in the interim.
- */
- TextureAtlas.prototype._addImage = function (index, image) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number.greaterThanOrEquals("index", index, 0);
- Check.defined("image", image);
- //>>includeEnd('debug');
-
- return new Promise((resolve, reject) => {
- this._imagesToAddQueue.push(
- new AddImageRequest({
- index,
- image,
- resolve,
- reject,
- }),
- );
-
- this._imagesToAddQueue.sort(
- ({ image: imageA }, { image: imageB }) =>
- imageB.height * imageB.width - imageA.height * imageA.width,
- );
- });
- };
-
- /**
- * Process the image queue for this frame, copying to the texture atlas and resizing the texture as needed.
- * @private
- * @param {Context} context The rendering context
- * @return {boolean} true if the texture was updated this frame
- */
- TextureAtlas.prototype._processImageQueue = function (context) {
- const queue = this._imagesToAddQueue;
- if (queue.length === 0) {
- return false;
- }
-
- this._rectangles.length = this._nextIndex;
-
- let i, error;
- for (i = 0; i < queue.length; ++i) {
- const imageRequest = queue[i];
- const { image, index } = imageRequest;
- const node = this._texturePacker.pack(index, image);
- if (!defined(node)) {
- // Atlas cannot fit all images in the queue
- // Bail early and resize
- try {
- this._resize(context, i);
- } catch (e) {
- error = e;
-
- if (defined(imageRequest.reject)) {
- imageRequest.reject(error);
- }
- }
- break;
- }
- this._rectangles[index] = node.rectangle;
- }
-
- if (defined(error)) {
- for (i = i + 1; i < queue.length; ++i) {
- const { resolve } = queue[i];
- if (defined(resolve)) {
- resolve(-1);
- }
- }
-
- queue.length = 0;
- return false;
- }
-
- for (let i = 0; i < queue.length; ++i) {
- this._copyImageToTexture(queue[i]);
- }
-
- queue.length = 0;
- return true;
- };
-
- /**
- * Processes any updates queued this frame, and updates rendering resources accordingly. Call before or after a frame has been rendered to avoid any race conditions for any dependant render commands.
- * @private
- * @param {Context} context The rendering context
- * @return {boolean} true if rendering resources were updated.
- */
- TextureAtlas.prototype.update = function (context) {
- if (!defined(this._texture)) {
- const width = this._initialSize.x;
- const height = this._initialSize.y;
- const pixelFormat = this._pixelFormat;
- const sampler = this._sampler;
- const borderPadding = this._borderWidthInPixels;
-
- this._texture = new Texture({
- context,
- width,
- height,
- pixelFormat,
- sampler,
- });
-
- this._texturePacker = new TexturePacker({
- height,
- width,
- borderPadding,
- });
- }
-
- return this._processImageQueue(context);
- };
-
- async function resolveImage(image, id) {
- if (typeof image === "function") {
- image = image(id);
- }
-
- if (typeof image === "string" || image instanceof Resource) {
- // Fetch the resource
- const resource = Resource.createIfNeeded(image);
- image = resource.fetchImage();
- }
-
- return image;
- }
-
- /**
- * Adds an image to the atlas. If the image is already in the atlas, the atlas is unchanged and
- * the existing index is used.
- * @private
- * @param {string} id An identifier to detect whether the image already exists in the atlas.
- * @param {HTMLImageElement|HTMLCanvasElement|string|Resource|Promise|TextureAtlas.CreateImageCallback} image An image or canvas to add to the texture atlas,
- * or a URL to an Image, or a Promise for an image, or a function that creates an image.
- * @param {number} width A number specifying the width of the texture. If undefined, the image width will be used.
- * @param {number} height A number specifying the height of the texture. If undefined, the image height will be used.
- * @returns {Promise<number> | number} The image region index or a promise that resolves to it. -1 is returned if resources are in the process of being destroyed.
- */
- TextureAtlas.prototype.addImage = function (id, image, width, height) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.string("id", id);
- Check.defined("image", image);
- //>>includeEnd('debug');
-
- let promise = this._indexPromiseById.get(id);
- let index = this._indexById.get(id);
- if (defined(promise)) {
- // This image is already being added
- return promise;
- }
- if (defined(index)) {
- // This image has already been added and resolved
- return index;
- }
-
- index = this._nextIndex++;
- this._indexById.set(id, index);
-
- const resolveAndAddImage = async () => {
- const resolvedImage = await resolveImage(image, id);
- //>>includeStart('debug', pragmas.debug);
- Check.defined("image", resolvedImage);
- //>>includeEnd('debug');
-
- if (this.isDestroyed() || !defined(resolvedImage)) {
- this._indexPromiseById.delete(id);
- return -1;
- }
-
- if (defined(width)) {
- resolvedImage.width = width;
- }
- if (defined(height)) {
- resolvedImage.height = height;
- }
-
- const imageIndex = await this._addImage(index, resolvedImage);
- this._indexPromiseById.delete(id);
- return imageIndex;
- };
-
- promise = resolveAndAddImage();
- this._indexPromiseById.set(id, promise);
- return promise;
- };
-
- /**
- * Get an existing sub-region of an existing atlas image as additional image indices.
- * @private
- * @param {string} id The identifier of the existing image.
- * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image.
- * @param {number} imageIndex The index of the image.
- * @returns {Promise<number> | number | undefined} The existing subRegion index, or undefined if not yet added.
- */
- TextureAtlas.prototype.getCachedImageSubRegion = function (
- id,
- subRegion,
- imageIndex,
- ) {
- const imagePromise = this._indexPromiseById.get(id);
- for (const [index, parentIndex] of this._subRegions.entries()) {
- if (imageIndex === parentIndex) {
- const boundingRegion = this._rectangles[index];
- if (boundingRegion.equals(subRegion)) {
- // The subregion is already being tracked
- if (imagePromise) {
- return imagePromise.then((resolvedImageIndex) =>
- resolvedImageIndex === -1 ? -1 : index,
- );
- }
- return index;
- }
- }
- }
- };
-
- /**
- * Add a sub-region of an existing atlas image as additional image indices.
- * @private
- * @param {string} id The identifier of the existing image.
- * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} defining a region of an existing image, measured in pixels from the bottom-left of the image.
- * @returns {number | Promise<number>} The resolved image region index, or a Promise that resolves to it. -1 is returned if resources are in the process of being destroyed.
- */
- TextureAtlas.prototype.addImageSubRegion = function (id, subRegion) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.string("id", id);
- Check.defined("subRegion", subRegion);
- //>>includeEnd('debug');
- const imageIndex = this._indexById.get(id);
- if (!defined(imageIndex)) {
- throw new RuntimeError(`image with id "${id}" not found in the atlas.`);
- }
-
- let index = this.getCachedImageSubRegion(id, subRegion, imageIndex);
- if (defined(index)) {
- return index;
- }
-
- index = this._nextIndex++;
- this._subRegions.set(index, imageIndex);
- this._rectangles[index] = subRegion.clone();
-
- const indexPromise =
- this._indexPromiseById.get(id) ?? Promise.resolve(imageIndex);
-
- return indexPromise.then((imageIndex) => {
- if (imageIndex === -1) {
- // The atlas has been destroyed
- return -1;
- }
-
- const rectangle = this._rectangles[imageIndex];
-
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number.lessThanOrEquals(
- "subRegion.x",
- subRegion.x,
- rectangle.width,
- );
- Check.typeOf.number.lessThanOrEquals(
- "subRegion.x + subRegion.width",
- subRegion.x + subRegion.width,
- rectangle.width,
- );
- Check.typeOf.number.lessThanOrEquals(
- "subRegion.y",
- subRegion.y,
- rectangle.height,
- );
- Check.typeOf.number.lessThanOrEquals(
- "subRegion.y + subRegion.height",
- subRegion.y + subRegion.height,
- rectangle.height,
- );
- //>>includeEnd('debug');
-
- return index;
- });
- };
-
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <br /><br />
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- * @private
- * @returns {boolean} True if this object was destroyed; otherwise, false.
- * @see TextureAtlas#destroy
- */
- TextureAtlas.prototype.isDestroyed = function () {
- return false;
- };
-
- /**
- * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
- * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
- * <br /><br />
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) to the object as done in the example.
- * @private
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
- * @example
- * atlas = atlas && atlas.destroy();
- * @see TextureAtlas#isDestroyed
- */
- TextureAtlas.prototype.destroy = function () {
- this._texture = this._texture && this._texture.destroy();
- this._imagesToAddQueue.forEach(({ resolve }) => {
- if (defined(resolve)) {
- resolve(-1);
- }
- });
-
- return destroyObject(this);
- };
-
- /**
- * A function that creates an image.
- * @private
- * @callback TextureAtlas.CreateImageCallback
- * @param {string} id The identifier of the image to load.
- * @returns {HTMLImageElement|Promise<HTMLImageElement>} The image, or a promise that will resolve to an image.
- */
-
- export default TextureAtlas;
|