123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- import BoundingRectangle from "../Core/BoundingRectangle.js";
- import Cartesian2 from "../Core/Cartesian2.js";
- import createGuid from "../Core/createGuid.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import PixelFormat from "../Core/PixelFormat.js";
- import Resource from "../Core/Resource.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import Framebuffer from "../Renderer/Framebuffer.js";
- import Texture from "../Renderer/Texture.js";
- import when from "../ThirdParty/when.js";
- function TextureAtlasNode(
- bottomLeft,
- topRight,
- childNode1,
- childNode2,
- imageIndex
- ) {
- this.bottomLeft = defaultValue(bottomLeft, Cartesian2.ZERO);
- this.topRight = defaultValue(topRight, Cartesian2.ZERO);
- this.childNode1 = childNode1;
- this.childNode2 = childNode2;
- this.imageIndex = imageIndex;
- }
- var defaultInitialSize = new Cartesian2(16.0, 16.0);
- function TextureAtlas(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- var borderWidthInPixels = defaultValue(options.borderWidthInPixels, 1.0);
- var initialSize = defaultValue(options.initialSize, defaultInitialSize);
-
- if (!defined(options.context)) {
- throw new DeveloperError("context is required.");
- }
- if (borderWidthInPixels < 0) {
- throw new DeveloperError(
- "borderWidthInPixels must be greater than or equal to zero."
- );
- }
- if (initialSize.x < 1 || initialSize.y < 1) {
- throw new DeveloperError("initialSize must be greater than zero.");
- }
-
- this._context = options.context;
- this._pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGBA);
- this._borderWidthInPixels = borderWidthInPixels;
- this._textureCoordinates = [];
- this._guid = createGuid();
- this._idHash = {};
- this._initialSize = initialSize;
- this._root = undefined;
- }
- Object.defineProperties(TextureAtlas.prototype, {
-
- borderWidthInPixels: {
- get: function () {
- return this._borderWidthInPixels;
- },
- },
-
- textureCoordinates: {
- get: function () {
- return this._textureCoordinates;
- },
- },
-
- texture: {
- get: function () {
- if (!defined(this._texture)) {
- this._texture = new Texture({
- context: this._context,
- width: this._initialSize.x,
- height: this._initialSize.y,
- pixelFormat: this._pixelFormat,
- });
- }
- return this._texture;
- },
- },
-
- numberOfImages: {
- get: function () {
- return this._textureCoordinates.length;
- },
- },
-
- guid: {
- get: function () {
- return this._guid;
- },
- },
- });
- function resizeAtlas(textureAtlas, image) {
- var context = textureAtlas._context;
- var numImages = textureAtlas.numberOfImages;
- var scalingFactor = 2.0;
- var borderWidthInPixels = textureAtlas._borderWidthInPixels;
- if (numImages > 0) {
- var oldAtlasWidth = textureAtlas._texture.width;
- var oldAtlasHeight = textureAtlas._texture.height;
- var atlasWidth =
- scalingFactor * (oldAtlasWidth + image.width + borderWidthInPixels);
- var atlasHeight =
- scalingFactor * (oldAtlasHeight + image.height + borderWidthInPixels);
- var widthRatio = oldAtlasWidth / atlasWidth;
- var heightRatio = oldAtlasHeight / atlasHeight;
-
- var nodeBottomRight = new TextureAtlasNode(
- new Cartesian2(oldAtlasWidth + borderWidthInPixels, borderWidthInPixels),
- new Cartesian2(atlasWidth, oldAtlasHeight)
- );
- var nodeBottomHalf = new TextureAtlasNode(
- new Cartesian2(),
- new Cartesian2(atlasWidth, oldAtlasHeight),
- textureAtlas._root,
- nodeBottomRight
- );
- var nodeTopHalf = new TextureAtlasNode(
- new Cartesian2(borderWidthInPixels, oldAtlasHeight + borderWidthInPixels),
- new Cartesian2(atlasWidth, atlasHeight)
- );
- var nodeMain = new TextureAtlasNode(
- new Cartesian2(),
- new Cartesian2(atlasWidth, atlasHeight),
- nodeBottomHalf,
- nodeTopHalf
- );
-
- for (var i = 0; i < textureAtlas._textureCoordinates.length; i++) {
- var texCoord = textureAtlas._textureCoordinates[i];
- if (defined(texCoord)) {
- texCoord.x *= widthRatio;
- texCoord.y *= heightRatio;
- texCoord.width *= widthRatio;
- texCoord.height *= heightRatio;
- }
- }
-
- var newTexture = new Texture({
- context: textureAtlas._context,
- width: atlasWidth,
- height: atlasHeight,
- pixelFormat: textureAtlas._pixelFormat,
- });
- var framebuffer = new Framebuffer({
- context: context,
- colorTextures: [textureAtlas._texture],
- destroyAttachments: false,
- });
- framebuffer._bind();
- newTexture.copyFromFramebuffer(0, 0, 0, 0, atlasWidth, atlasHeight);
- framebuffer._unBind();
- framebuffer.destroy();
- textureAtlas._texture =
- textureAtlas._texture && textureAtlas._texture.destroy();
- textureAtlas._texture = newTexture;
- textureAtlas._root = nodeMain;
- } else {
-
- var initialWidth = scalingFactor * (image.width + 2 * borderWidthInPixels);
- var initialHeight =
- scalingFactor * (image.height + 2 * borderWidthInPixels);
- if (initialWidth < textureAtlas._initialSize.x) {
- initialWidth = textureAtlas._initialSize.x;
- }
- if (initialHeight < textureAtlas._initialSize.y) {
- initialHeight = textureAtlas._initialSize.y;
- }
- textureAtlas._texture =
- textureAtlas._texture && textureAtlas._texture.destroy();
- textureAtlas._texture = new Texture({
- context: textureAtlas._context,
- width: initialWidth,
- height: initialHeight,
- pixelFormat: textureAtlas._pixelFormat,
- });
- textureAtlas._root = new TextureAtlasNode(
- new Cartesian2(borderWidthInPixels, borderWidthInPixels),
- new Cartesian2(initialWidth, initialHeight)
- );
- }
- }
- function findNode(textureAtlas, node, image) {
- if (!defined(node)) {
- return undefined;
- }
-
- if (!defined(node.childNode1) && !defined(node.childNode2)) {
-
- if (defined(node.imageIndex)) {
- return undefined;
- }
- var nodeWidth = node.topRight.x - node.bottomLeft.x;
- var nodeHeight = node.topRight.y - node.bottomLeft.y;
- var widthDifference = nodeWidth - image.width;
- var heightDifference = nodeHeight - image.height;
-
- if (widthDifference < 0 || heightDifference < 0) {
- return undefined;
- }
-
- if (widthDifference === 0 && heightDifference === 0) {
- return node;
- }
-
- if (widthDifference > heightDifference) {
- node.childNode1 = new TextureAtlasNode(
- new Cartesian2(node.bottomLeft.x, node.bottomLeft.y),
- new Cartesian2(node.bottomLeft.x + image.width, node.topRight.y)
- );
-
- var childNode2BottomLeftX =
- node.bottomLeft.x + image.width + textureAtlas._borderWidthInPixels;
- if (childNode2BottomLeftX < node.topRight.x) {
- node.childNode2 = new TextureAtlasNode(
- new Cartesian2(childNode2BottomLeftX, node.bottomLeft.y),
- new Cartesian2(node.topRight.x, node.topRight.y)
- );
- }
- }
-
- else {
- node.childNode1 = new TextureAtlasNode(
- new Cartesian2(node.bottomLeft.x, node.bottomLeft.y),
- new Cartesian2(node.topRight.x, node.bottomLeft.y + image.height)
- );
-
- var childNode2BottomLeftY =
- node.bottomLeft.y + image.height + textureAtlas._borderWidthInPixels;
- if (childNode2BottomLeftY < node.topRight.y) {
- node.childNode2 = new TextureAtlasNode(
- new Cartesian2(node.bottomLeft.x, childNode2BottomLeftY),
- new Cartesian2(node.topRight.x, node.topRight.y)
- );
- }
- }
- return findNode(textureAtlas, node.childNode1, image);
- }
-
- return (
- findNode(textureAtlas, node.childNode1, image) ||
- findNode(textureAtlas, node.childNode2, image)
- );
- }
- function addImage(textureAtlas, image, index) {
- var node = findNode(textureAtlas, textureAtlas._root, image);
- if (defined(node)) {
-
- node.imageIndex = index;
-
- var atlasWidth = textureAtlas._texture.width;
- var atlasHeight = textureAtlas._texture.height;
- var nodeWidth = node.topRight.x - node.bottomLeft.x;
- var nodeHeight = node.topRight.y - node.bottomLeft.y;
- var x = node.bottomLeft.x / atlasWidth;
- var y = node.bottomLeft.y / atlasHeight;
- var w = nodeWidth / atlasWidth;
- var h = nodeHeight / atlasHeight;
- textureAtlas._textureCoordinates[index] = new BoundingRectangle(x, y, w, h);
- textureAtlas._texture.copyFrom(image, node.bottomLeft.x, node.bottomLeft.y);
- } else {
-
- resizeAtlas(textureAtlas, image);
- addImage(textureAtlas, image, index);
- }
- textureAtlas._guid = createGuid();
- }
- TextureAtlas.prototype.addImage = function (id, image) {
-
- if (!defined(id)) {
- throw new DeveloperError("id is required.");
- }
- if (!defined(image)) {
- throw new DeveloperError("image is required.");
- }
-
- var indexPromise = this._idHash[id];
- if (defined(indexPromise)) {
-
- return indexPromise;
- }
-
- if (typeof image === "function") {
-
- image = image(id);
-
- if (!defined(image)) {
- throw new DeveloperError("image is required.");
- }
-
- } else if (typeof image === "string" || image instanceof Resource) {
-
- var resource = Resource.createIfNeeded(image);
- image = resource.fetchImage();
- }
- var that = this;
- indexPromise = when(image, function (image) {
- if (that.isDestroyed()) {
- return -1;
- }
- var index = that.numberOfImages;
- addImage(that, image, index);
- return index;
- });
-
- this._idHash[id] = indexPromise;
- return indexPromise;
- };
- TextureAtlas.prototype.addSubRegion = function (id, subRegion) {
-
- if (!defined(id)) {
- throw new DeveloperError("id is required.");
- }
- if (!defined(subRegion)) {
- throw new DeveloperError("subRegion is required.");
- }
-
- var indexPromise = this._idHash[id];
- if (!defined(indexPromise)) {
- throw new RuntimeError(
- 'image with id "' + id + '" not found in the atlas.'
- );
- }
- var that = this;
- return when(indexPromise, function (index) {
- if (index === -1) {
-
- return -1;
- }
- var atlasWidth = that._texture.width;
- var atlasHeight = that._texture.height;
- var numImages = that.numberOfImages;
- var baseRegion = that._textureCoordinates[index];
- var x = baseRegion.x + subRegion.x / atlasWidth;
- var y = baseRegion.y + subRegion.y / atlasHeight;
- var w = subRegion.width / atlasWidth;
- var h = subRegion.height / atlasHeight;
- that._textureCoordinates.push(new BoundingRectangle(x, y, w, h));
- that._guid = createGuid();
- return numImages;
- });
- };
- TextureAtlas.prototype.isDestroyed = function () {
- return false;
- };
- TextureAtlas.prototype.destroy = function () {
- this._texture = this._texture && this._texture.destroy();
- return destroyObject(this);
- };
- export default TextureAtlas;
|