[engine] 新动态图集实现

This commit is contained in:
SmallMain 2022-06-25 00:48:16 +08:00
parent 9fca60746c
commit 8b0ef377a9
10 changed files with 815 additions and 172 deletions

View File

@ -95,7 +95,7 @@ var builtins = {
effect.addRef(); effect.addRef();
cc.sp.inited = true; cc.sp.inited = true;
cc.sp.multiBatcher.init(); cc.sp.multiBatcher.init();
if (cc.dynamicAtlasManager.maxAtlasCount === -1) cc.dynamicAtlasManager.maxAtlasCount = cc.sp.MAX_MULTITEXTURE_NUM;
cb(); cb();
}); });
}, },

View File

@ -828,6 +828,11 @@ let SpriteFrame = cc.Class(/** @lends cc.SpriteFrame# */{
handle.result.push(this, '_textureSetter', textureUuid); handle.result.push(this, '_textureSetter', textureUuid);
} }
} }
},
destroy() {
cc.dynamicAtlasManager && cc.dynamicAtlasManager.deleteSpriteFrame(this);
this._super();
} }
}); });

View File

@ -478,7 +478,7 @@ let Label = cc.Class({
if (this.cacheMode === oldValue) return; if (this.cacheMode === oldValue) return;
if (oldValue === CacheMode.BITMAP && !(this.font instanceof cc.BitmapFont)) { if (oldValue === CacheMode.BITMAP && !(this.font instanceof cc.BitmapFont)) {
this._frame && this._frame._resetDynamicAtlasFrame(); deleteFromDynamicAtlas(this, this._frame);
} }
if (oldValue === CacheMode.CHAR) { if (oldValue === CacheMode.CHAR) {
@ -583,6 +583,10 @@ let Label = cc.Class({
type: RenderComponent.EnableType, type: RenderComponent.EnableType,
default: RenderComponent.EnableType.GLOBAL, default: RenderComponent.EnableType.GLOBAL,
}, },
allowDynamicAtlas: {
type: RenderComponent.EnableType,
default: RenderComponent.EnableType.GLOBAL,
},
}, },
statics: { statics: {
@ -641,9 +645,12 @@ let Label = cc.Class({
this._assemblerData = null; this._assemblerData = null;
this._letterTexture = null; this._letterTexture = null;
if (this._ttfTexture) { if (this._ttfTexture) {
// HACK 由于会出现多个 uuid 一样的情况,销毁时会将动态图集中的区域直接销毁,导致其它使用该区域的 Label 显示错误,所以先将 packable = false不走销毁逻辑在下方走 frame 的回收逻辑
this._ttfTexture._packable = false;
this._ttfTexture.destroy(); this._ttfTexture.destroy();
this._ttfTexture = null; this._ttfTexture = null;
} }
this._resetFrame();
this._super(); this._super();
}, },
@ -760,7 +767,7 @@ let Label = cc.Class({
} }
if (this.cacheMode !== CacheMode.CHAR) { if (this.cacheMode !== CacheMode.CHAR) {
this._frame._resetDynamicAtlasFrame(); deleteFromDynamicAtlas(this, this._frame);
this._frame._refreshTexture(this._ttfTexture); this._frame._refreshTexture(this._ttfTexture);
if (this._srcBlendFactor === cc.macro.BlendFactor.ONE && !CC_NATIVERENDERER) { if (this._srcBlendFactor === cc.macro.BlendFactor.ONE && !CC_NATIVERENDERER) {
this._ttfTexture.setPremultiplyAlpha(true); this._ttfTexture.setPremultiplyAlpha(true);

View File

@ -374,7 +374,31 @@ let RichText = cc.Class({
} }
} }
} }
} },
allowDynamicAtlas: {
type: RenderComponent.EnableType,
default: RenderComponent.EnableType.GLOBAL,
notify: function (oldValue) {
if (this.allowDynamicAtlas === oldValue) return;
for (let i = 0; i < this._labelSegments.length; i++) {
const labelComponent = this._labelSegments[i].getComponent(cc.Label);
if (labelComponent) {
labelComponent.allowDynamicAtlas = this.allowDynamicAtlas;
}
const spriteComponent = this._labelSegments[i].getComponent(cc.Sprite);
if (spriteComponent) {
spriteComponent.allowDynamicAtlas = this.allowDynamicAtlas;
}
}
for (let i = 0; i < this._labelSegmentsCache.length; i++) {
const labelComponent = this._labelSegmentsCache[i].getComponent(cc.Label);
if (labelComponent) {
labelComponent.allowDynamicAtlas = this.allowDynamicAtlas;
}
}
}
},
}, },
statics: { statics: {
@ -682,6 +706,8 @@ let RichText = cc.Class({
let spriteComponent = spriteNode.addComponent(cc.Sprite); let spriteComponent = spriteNode.addComponent(cc.Sprite);
spriteComponent.autoSwitchMaterial = this.autoSwitchMaterial; spriteComponent.autoSwitchMaterial = this.autoSwitchMaterial;
spriteComponent.allowDynamicAtlas = this.allowDynamicAtlas;
switch (richTextElement.style.imageAlign) switch (richTextElement.style.imageAlign)
{ {
case 'top': case 'top':
@ -975,6 +1001,8 @@ let RichText = cc.Class({
labelComponent.cacheMode = this.cacheMode; labelComponent.cacheMode = this.cacheMode;
labelComponent.autoSwitchMaterial = this.autoSwitchMaterial; labelComponent.autoSwitchMaterial = this.autoSwitchMaterial;
labelComponent.allowDynamicAtlas = this.allowDynamicAtlas;
let isAsset = this.font instanceof cc.Font; let isAsset = this.font instanceof cc.Font;
if (isAsset && !this._isSystemFontUsed) { if (isAsset && !this._isSystemFontUsed) {
labelComponent.font = this.font; labelComponent.font = this.font;

View File

@ -392,6 +392,10 @@ var Sprite = cc.Class({
type: RenderComponent.EnableType, type: RenderComponent.EnableType,
default: RenderComponent.EnableType.GLOBAL, default: RenderComponent.EnableType.GLOBAL,
}, },
allowDynamicAtlas: {
type: RenderComponent.EnableType,
default: RenderComponent.EnableType.GLOBAL,
},
}, },
statics: { statics: {

View File

@ -27,12 +27,15 @@ export default class Assembler {
packDynamicAtlasAndCheckMaterial(comp, frame) { packDynamicAtlasAndCheckMaterial(comp, frame) {
if (CC_TEST) return false; if (CC_TEST) return false;
const allowDynamicAtlas = comp.allowDynamicAtlas;
if ((cc.sp.allowDynamicAtlas && allowDynamicAtlas === 0) || allowDynamicAtlas === 1) {
if (!frame._original && dynamicAtlasManager && frame._texture.packable && frame._texture.loaded) { if (!frame._original && dynamicAtlasManager && frame._texture.packable && frame._texture.loaded) {
let packedFrame = dynamicAtlasManager.insertSpriteFrame(frame); let packedFrame = dynamicAtlasManager.insertSpriteFrame(frame);
if (packedFrame) { if (packedFrame) {
frame._setDynamicAtlasFrame(packedFrame); frame._setDynamicAtlasFrame(packedFrame);
} }
} }
}
const material = comp._materials[0]; const material = comp._materials[0];
if (!material) return false; if (!material) return false;

View File

@ -1,140 +1,5 @@
const RenderTexture = require('../../../assets/CCRenderTexture'); // 保留备用
function Atlas(width, height) {
const space = 2;
function Atlas (width, height) {
let texture = new RenderTexture();
texture.initWithSize(width, height);
texture.update();
this._texture = texture;
this._x = space;
this._y = space;
this._nexty = space;
this._width = width;
this._height = height;
this._innerTextureInfos = {};
this._innerSpriteFrames = [];
this._count = 0;
} }
Atlas.DEFAULT_HASH = (new RenderTexture())._getHash();
cc.js.mixin(Atlas.prototype, {
insertSpriteFrame (spriteFrame) {
let rect = spriteFrame._rect,
texture = spriteFrame._texture,
info = this._innerTextureInfos[texture._id];
let sx = rect.x, sy = rect.y;
if (info) {
sx += info.x;
sy += info.y;
}
else {
let width = texture.width, height = texture.height;
if ((this._x + width + space) > this._width) {
this._x = space;
this._y = this._nexty;
}
if ((this._y + height + space) > this._nexty) {
this._nexty = this._y + height + space;
}
if (this._nexty > this._height) {
return null;
}
// texture bleeding
if (cc.dynamicAtlasManager.textureBleeding) {
// Smaller frame is more likely to be affected by linear filter
if (width <= 8 || height <= 8) {
this._texture.drawTextureAt(texture, this._x-1, this._y-1);
this._texture.drawTextureAt(texture, this._x-1, this._y+1);
this._texture.drawTextureAt(texture, this._x+1, this._y-1);
this._texture.drawTextureAt(texture, this._x+1, this._y+1);
}
this._texture.drawTextureAt(texture, this._x-1, this._y);
this._texture.drawTextureAt(texture, this._x+1, this._y);
this._texture.drawTextureAt(texture, this._x, this._y-1);
this._texture.drawTextureAt(texture, this._x, this._y+1);
}
this._texture.drawTextureAt(texture, this._x, this._y);
this._innerTextureInfos[texture._id] = {
x: this._x,
y: this._y,
texture: texture
};
this._count++;
sx += this._x;
sy += this._y;
this._x += width + space;
this._dirty = true;
}
let frame = {
x: sx,
y: sy,
texture: this._texture
}
this._innerSpriteFrames.push(spriteFrame);
return frame;
},
update () {
if (!this._dirty) return;
this._texture.update();
this._dirty = false;
},
deleteInnerTexture (texture) {
if (texture && this._innerTextureInfos[texture._id]) {
delete this._innerTextureInfos[texture._id];
this._count--;
}
},
isEmpty () {
return this._count <= 0;
},
reset () {
this._x = space;
this._y = space;
this._nexty = space;
let frames = this._innerSpriteFrames;
for (let i = 0, l = frames.length; i < l; i++) {
let frame = frames[i];
if (!frame.isValid) {
continue;
}
frame._resetDynamicAtlasFrame();
}
this._innerSpriteFrames.length = 0;
this._innerTextureInfos = {};
},
destroy () {
this.reset();
this._texture.destroy();
}
});
module.exports = Atlas; module.exports = Atlas;

View File

@ -1,12 +1,15 @@
const Atlas = require('./atlas'); // const Atlas = require('./atlas');
import { Atlas, Rect } from './reusable-atlas';
let _atlases = []; let _atlases = [];
let _atlasIndex = -1; let _atlasIndex = -1;
let _maxAtlasCount = 5; let _maxAtlasCount = -1;
let _textureSize = 2048; let _textureSize = 2048;
let _maxFrameSize = 512; let _maxFrameSize = 512;
let _textureBleeding = true; let _textureBleeding = true;
let _autoMultiBatch = true;
let _autoResetBeforeSceneLoad = true;
let _debugNode = null; let _debugNode = null;
@ -15,12 +18,15 @@ function newAtlas () {
if (!atlas) { if (!atlas) {
atlas = new Atlas(_textureSize, _textureSize); atlas = new Atlas(_textureSize, _textureSize);
_atlases.push(atlas); _atlases.push(atlas);
if (dynamicAtlasManager.autoMultiBatch) cc.sp.multiBatcher.requsetMaterial(atlas._texture);
} }
return atlas; return atlas;
} }
function beforeSceneLoad () { function beforeSceneLoad() {
dynamicAtlasManager.reset(); if (_autoResetBeforeSceneLoad) {
dynamicAtlasManager.reset();
}
} }
let _enabled = false; let _enabled = false;
@ -32,6 +38,7 @@ let _enabled = false;
*/ */
let dynamicAtlasManager = { let dynamicAtlasManager = {
Atlas: Atlas, Atlas: Atlas,
Rect: Rect,
/** /**
* !#en Enable or disable the dynamic atlas, see [Dynamic Atlas](https://docs.cocos.com/creator/manual/en/advanced-topics/dynamic-atlas.html) for details. * !#en Enable or disable the dynamic atlas, see [Dynamic Atlas](https://docs.cocos.com/creator/manual/en/advanced-topics/dynamic-atlas.html) for details.
@ -39,10 +46,10 @@ let dynamicAtlasManager = {
* @property enabled * @property enabled
* @type {Boolean} * @type {Boolean}
*/ */
get enabled () { get enabled() {
return _enabled; return _enabled;
}, },
set enabled (value) { set enabled(value) {
if (_enabled === value) return; if (_enabled === value) return;
if (value) { if (value) {
@ -62,10 +69,10 @@ let dynamicAtlasManager = {
* @property maxAtlasCount * @property maxAtlasCount
* @type {Number} * @type {Number}
*/ */
get maxAtlasCount () { get maxAtlasCount() {
return _maxAtlasCount; return _maxAtlasCount;
}, },
set maxAtlasCount (value) { set maxAtlasCount(value) {
_maxAtlasCount = value; _maxAtlasCount = value;
}, },
@ -75,7 +82,7 @@ let dynamicAtlasManager = {
* @property atlasCount * @property atlasCount
* @type {Number} * @type {Number}
*/ */
get atlasCount () { get atlasCount() {
return _atlases.length; return _atlases.length;
}, },
@ -85,11 +92,11 @@ let dynamicAtlasManager = {
* @property textureBleeding * @property textureBleeding
* @type {Boolean} * @type {Boolean}
*/ */
get textureBleeding () { get textureBleeding() {
return _textureBleeding; return _textureBleeding;
}, },
set textureBleeding (enable) { set textureBleeding(enable) {
_textureBleeding = enable; _textureBleeding = enable;
}, },
@ -99,10 +106,10 @@ let dynamicAtlasManager = {
* @property textureSize * @property textureSize
* @type {Number} * @type {Number}
*/ */
get textureSize () { get textureSize() {
return _textureSize; return _textureSize;
}, },
set textureSize (value) { set textureSize(value) {
_textureSize = value; _textureSize = value;
}, },
@ -112,13 +119,65 @@ let dynamicAtlasManager = {
* @property maxFrameSize * @property maxFrameSize
* @type {Number} * @type {Number}
*/ */
get maxFrameSize () { get maxFrameSize() {
return _maxFrameSize; return _maxFrameSize;
}, },
set maxFrameSize (value) { set maxFrameSize(value) {
_maxFrameSize = value; _maxFrameSize = value;
}, },
/**
* !#en Is enable autoMultiBatch.
* !#zh 是否开启自动多纹理合批
* @property autoMultiBatch
* @type {Boolean}
*/
get autoMultiBatch() {
return _autoMultiBatch;
},
set autoMultiBatch(enable) {
if (_autoMultiBatch === enable) return;
if (enable) {
for (let i = 0, l = _atlases.length; i < l; i++) {
cc.sp.multiBatcher.requsetMaterial(_atlases[i]._texture);
}
}
_autoMultiBatch = enable;
},
/**
* !#en Is enable autoResetBeforeSceneLoad.
* !#zh 是否在场景切换时清空所有图集
* @property autoResetBeforeSceneLoad
* @type {Boolean}
*/
get autoResetBeforeSceneLoad() {
return _autoResetBeforeSceneLoad;
},
set autoResetBeforeSceneLoad(enable) {
if (_autoResetBeforeSceneLoad === enable) return;
_autoResetBeforeSceneLoad = enable;
},
/**
* !#en atlases
* !#zh 图集数组
* @property atlases
* @type {Atlas}
*/
get atlases() {
return _atlases;
},
/**
* 已用空间集合
*/
rects: Object.create(null),
/** /**
* !#en The minimum size of the picture that can be added to the atlas. * !#en The minimum size of the picture that can be added to the atlas.
* !#zh 可以添加进图集的图片的最小尺寸 * !#zh 可以添加进图集的图片的最小尺寸
@ -133,26 +192,77 @@ let dynamicAtlasManager = {
* @method insertSpriteFrame * @method insertSpriteFrame
* @param {SpriteFrame} spriteFrame * @param {SpriteFrame} spriteFrame
*/ */
insertSpriteFrame (spriteFrame) { insertSpriteFrame(spriteFrame) {
if (CC_EDITOR) return null; if (CC_EDITOR) return null;
if (!_enabled || _atlasIndex === _maxAtlasCount || if (!_enabled || !spriteFrame || spriteFrame._original) return null;
!spriteFrame || spriteFrame._original) return null;
if (!spriteFrame._texture.packable) return null; let atlas, frame;
let atlas = _atlases[_atlasIndex]; // 是否贴图已经在图集中
if (!atlas) { let rect = spriteFrame._rect,
atlas = newAtlas(); texture = spriteFrame._texture,
info = this.rects[texture._uuid];
let sx = rect.x, sy = rect.y;
if (info) {
sx += info.x;
sy += info.y;
info.spriteFrames.push(spriteFrame);
frame = {
x: sx,
y: sy,
texture: info.atlas._texture,
};
return frame;
} }
let frame = atlas.insertSpriteFrame(spriteFrame); // 尝试加入已有图集
if (!frame && _atlasIndex !== _maxAtlasCount) { for (let i = 0; i <= _atlasIndex; i++) {
atlas = _atlases[i];
frame = atlas.insertSpriteFrame(spriteFrame);
if (frame) {
return frame;
}
}
// 创建新图集尝试加入
if (_atlasIndex + 1 < _maxAtlasCount) {
atlas = newAtlas(); atlas = newAtlas();
return atlas.insertSpriteFrame(spriteFrame); return atlas.insertSpriteFrame(spriteFrame);
} }
return frame; return frame;
}, },
/**
* !#en Delete a sprite frame from the dynamic atlas.
* !#zh 使精灵帧取消使用动态图集
* @method deleteSpriteFrame
* @param {SpriteFrame} spriteFrame
*/
deleteSpriteFrame(spriteFrame) {
if (spriteFrame && !CC_TEST) {
if (spriteFrame._original) {
this.deleteAtlasSpriteFrame(spriteFrame);
spriteFrame._resetDynamicAtlasFrame();
}
}
},
/**
* !#en Delete a texture from the dynamic atlas.
* !#zh 从动态图集删除该贴图使用该贴图的精灵帧会被还原
* @method deleteTexture
* @param {Texture2D} texture
*/
deleteTexture(texture) {
this.deleteAtlasTexture(texture);
},
/** /**
* !#en Resets all dynamic atlas, and the existing ones will be destroyed. * !#en Resets all dynamic atlas, and the existing ones will be destroyed.
* !#zh 重置所有动态图集已有的动态图集会被销毁 * !#zh 重置所有动态图集已有的动态图集会被销毁
@ -170,18 +280,18 @@ let dynamicAtlasManager = {
if (!spriteFrame._original) return; if (!spriteFrame._original) return;
let texture = spriteFrame._original._texture; let texture = spriteFrame._original._texture;
this.deleteAtlasTexture(texture); for (let i = _atlases.length - 1; i >= 0; i--) {
if (_atlases[i].deleteSpriteFrame(texture, spriteFrame)) {
return;
}
}
}, },
deleteAtlasTexture (texture) { deleteAtlasTexture (texture) {
if (texture) { if (texture) {
for (let i = _atlases.length - 1; i >= 0; i--) { for (let i = _atlases.length - 1; i >= 0; i--) {
_atlases[i].deleteInnerTexture(texture); if (_atlases[i].deleteInnerTexture(texture, true)) {
return;
if (_atlases[i].isEmpty()) {
_atlases[i].destroy();
_atlases.splice(i, 1);
_atlasIndex--;
} }
} }
} }

View File

@ -0,0 +1,593 @@
// @ts-expect-error
const RenderTexture = require('../../../assets/CCRenderTexture');
/**
*
*/
export class Rect {
/**
*
*/
static pool: Rect[] = [];
/**
*
*/
static pointer: number = 0;
/**
*
*/
static reuse(atlas: Atlas, width: number, height: number, x: number, y: number) {
if (this.pointer === 0) {
for (let i = 0; i < 128; i++) {
Rect.pool[i] = new Rect(atlas, 0, 0, 0, 0);
}
this.pointer += 128;
}
this.pointer--;
const rect = this.pool[this.pointer];
rect.atlas = atlas;
rect.width = width;
rect.height = height;
rect.x = x;
rect.y = y;
return rect;
}
/**
*
*/
static recycle(rect: Rect) {
rect.atlas = undefined!;
rect.uuid = "";
rect.spriteFrames.length = 0;
rect.parentRect = undefined;
rect.subRectA = undefined;
rect.subRectB = undefined;
rect.subRectC = undefined;
rect.cacheIndex = -1;
this.pool[this.pointer] = rect;
this.pointer++;
}
/**
* Atlas
*/
atlas: Atlas;
/**
*
*/
width: number = 0;
/**
*
*/
height: number = 0;
/**
*
*/
x: number = 0;
/**
*
*/
y: number = 0;
/**
* freeRects
*/
cacheIndex: number = -1;
/**
* cc.Texture2D UUID
*/
uuid: string = '';
/**
* 使
*/
spriteFrames: any[] = [];
/**
*
*/
parentRect: Rect | undefined;
/**
*
*/
subRectA: Rect | undefined;
/**
*
*/
subRectB: Rect | undefined;
/**
*
*/
subRectC: Rect | undefined;
/**
*
*/
used: number = 0;
/**
*
*/
get sizes() {
return this.width * this.height;
}
constructor(atlas: Atlas, width: number, height: number, x: number, y: number) {
this.atlas = atlas;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
}
/**
*
*
*
* 退
*/
export class Atlas {
/**
*
*/
static ignoreRectSize: number = 10;
/**
* Atlas
*/
static DEFAULT_HASH = (new RenderTexture())._getHash();
/**
*
*/
width: number = 0;
/**
*
*/
height: number = 0;
/**
*
*/
padding: number = 0;
/**
*
*/
border: number = 0;
/**
*
*/
rootRect: Rect;
/**
*
*/
freeRects: Rect[] = [];
/**
* 使
*/
_count = 0;
/**
* cc.RenderTexture
*/
_texture: any;
/**
* texture update dirty
*/
_dirty: boolean = false;
constructor(width: number, height: number, padding: number = 2, border: number = 2) {
const texture = new RenderTexture();
texture.initWithSize(width, height);
texture.update();
this._texture = texture;
this.width = width;
this.height = height;
this.padding = padding;
this.border = border;
this.rootRect = Rect.reuse(
this,
this.width + this.padding - this.border * 2,
this.height + this.padding - this.border * 2,
this.border,
this.border,
);
this.pushFreeRect(this.rootRect);
}
/**
* push to free rects
*/
protected pushFreeRect(rect: Rect) {
const i = this.freeRects.push(rect) - 1;
rect.cacheIndex = i;
}
/**
* faster remove from free rects
*/
protected removeFreeRect(index: number) {
const temp = this.freeRects[index];
const temp2 = this.freeRects[this.freeRects.length - 1];
temp2.cacheIndex = index;
temp.cacheIndex = -1;
this.freeRects[index] = temp2;
this.freeRects.pop();
}
/**
* change member from free rects
*/
protected replaceFreeRect(index: number, rect: Rect) {
this.freeRects[index].cacheIndex = -1;
rect.cacheIndex = index;
this.freeRects[index] = rect;
}
/**
* SpriteFrame
*/
insertSpriteFrame(spriteFrame: any) {
let rect = spriteFrame._rect,
texture = spriteFrame._texture;
let sx = rect.x, sy = rect.y;
let width = texture.width, height = texture.height;
const result = this.insert(texture);
if (!result) {
return null;
}
// texture bleeding
if (cc.dynamicAtlasManager.textureBleeding) {
// Smaller frame is more likely to be affected by linear filter
if (width <= 8 || height <= 8) {
this._texture.drawTextureAt(texture, result.x - 1, result.y - 1);
this._texture.drawTextureAt(texture, result.x - 1, result.y + 1);
this._texture.drawTextureAt(texture, result.x + 1, result.y - 1);
this._texture.drawTextureAt(texture, result.x + 1, result.y + 1);
}
this._texture.drawTextureAt(texture, result.x - 1, result.y);
this._texture.drawTextureAt(texture, result.x + 1, result.y);
this._texture.drawTextureAt(texture, result.x, result.y - 1);
this._texture.drawTextureAt(texture, result.x, result.y + 1);
}
this._texture.drawTextureAt(texture, result.x, result.y);
this._count++;
sx += result.x;
sy += result.y;
result.spriteFrames.push(spriteFrame);
this._dirty = true;
let frame = {
x: sx,
y: sy,
texture: this._texture,
};
return frame;
}
/**
*
*/
insert(texture: any) {
const width = texture.width + this.padding, height = texture.height + this.padding;
let score = Number.MAX_VALUE;
let areaFit = 0;
let original: Rect | undefined = undefined;
let originalIndex = 0;
// 查找足够容纳的空区域
for (let i = 0; i < this.freeRects.length; i++) {
const rect = this.freeRects[i];
if (rect.width >= width && rect.height >= height) {
areaFit = rect.sizes - width * height;
if (areaFit < score) {
original = rect;
originalIndex = i;
score = areaFit;
}
}
}
// 切割空区域
if (original) {
if (original.width === width && original.height === height) {
original.uuid = texture._uuid;
original.used++;
if (original.parentRect) original.parentRect.used++;
cc.dynamicAtlasManager.rects[texture._uuid] = original;
this.removeFreeRect(originalIndex);
return original;
}
const best = Rect.reuse(this, width, height, original.x, original.y);
let tmp: Rect;
if (best.y + best.height < original.y + original.height) {
tmp = Rect.reuse(
this,
original.width,
original.y + original.height - (best.y + best.height),
original.x,
best.y + best.height,
);
tmp.parentRect = original;
original.subRectB = tmp;
if (tmp.width > Atlas.ignoreRectSize && tmp.height > Atlas.ignoreRectSize) {
// 替换旧区域
this.replaceFreeRect(originalIndex, tmp);
originalIndex = -1;
}
}
if (best.x + best.width < original.x + original.width) {
tmp = Rect.reuse(
this,
original.x + original.width - (best.x + best.width),
original.height - (original.y + original.height - (best.y + best.height)),
best.x + best.width,
original.y,
);
tmp.parentRect = original;
original.subRectC = tmp;
if (tmp.width > Atlas.ignoreRectSize && tmp.height > Atlas.ignoreRectSize) {
if (originalIndex !== -1) {
// 替换旧区域
this.replaceFreeRect(originalIndex, tmp);
originalIndex = -1;
} else {
this.pushFreeRect(tmp);
}
}
}
if (originalIndex !== -1) {
this.removeFreeRect(originalIndex);
}
best.parentRect = original;
original.subRectA = best;
best.used++;
original.used++;
if (original.used === 1 && original.parentRect) original.parentRect.used++;
best.uuid = texture._uuid;
cc.dynamicAtlasManager.rects[texture._uuid] = best;
return best;
} else {
return undefined;
}
}
/**
* update texture
*/
update() {
if (!this._dirty) return;
this._texture.update();
this._dirty = false;
}
/**
*
*/
deleteSpriteFrame(texture: any, frame: any) {
if (texture) {
const rect: Rect | undefined = cc.dynamicAtlasManager.rects[texture._uuid];
if (rect) {
const index = rect.spriteFrames.indexOf(frame);
if (index !== -1) {
rect.spriteFrames.splice(index, 1);
// 判断如果没有引用则删除 Texture
if (rect.spriteFrames.length === 0) {
rect.atlas.deleteInnerRect(rect);
}
} else {
cc.warn('[Dynamic Atlas] can\'t find spriteFrame in Rect.');
}
return true;
}
}
return false;
}
/**
*
*/
deleteInnerRect(rect: Rect) {
delete cc.dynamicAtlasManager.rects[rect.uuid];
rect.uuid = "";
this._count--;
// 还原 SpriteFrame
for (const spriteFrame of rect.spriteFrames) {
if (spriteFrame.isValid) {
spriteFrame._resetDynamicAtlasFrame();
}
}
rect.spriteFrames.length = 0;
this.tryMergeRecycle(rect);
}
/**
*
*/
deleteInnerTexture(texture: any) {
if (texture) {
const rect: Rect | undefined = cc.dynamicAtlasManager.rects[texture._uuid];
if (rect) {
rect.atlas.deleteInnerRect(rect);
return true;
}
}
return false;
}
/**
*
*/
protected tryMergeRecycle(rect: Rect) {
let old: Rect | undefined = undefined;
let parent: Rect | undefined = rect;
while (parent) {
parent.used--;
if (parent.used === 0) {
// 回收所有子矩形
if (parent.subRectA) {
// 可能是 ignoreRect
const i = parent.subRectA.cacheIndex;
if (i !== -1) {
this.removeFreeRect(i);
}
Rect.recycle(parent.subRectA);
parent.subRectA = undefined;
}
if (parent.subRectB) {
const i = parent.subRectB.cacheIndex;
if (i !== -1) {
this.removeFreeRect(i);
}
Rect.recycle(parent.subRectB);
parent.subRectB = undefined;
}
if (parent.subRectC) {
const i = parent.subRectC.cacheIndex;
if (i !== -1) {
this.removeFreeRect(i);
}
Rect.recycle(parent.subRectC);
parent.subRectC = undefined;
}
old = parent;
parent = parent.parentRect;
} else {
if (old) {
if (old.width > Atlas.ignoreRectSize && old.height > Atlas.ignoreRectSize) {
this.pushFreeRect(old);
}
}
old = parent;
parent = undefined;
}
}
if (old === this.rootRect && old.used === 0) {
this.pushFreeRect(old);
}
}
/**
* 使
*/
isEmpty() {
return this._count <= 0;
}
/**
*
*/
reset() {
const rects = cc.dynamicAtlasManager.rects;
for (const key in rects) {
const rect: Rect = rects[key];
if (rect.atlas === this) {
delete rects[key];
for (const spriteFrame of rect.spriteFrames) {
if (spriteFrame.isValid) {
spriteFrame._resetDynamicAtlasFrame();
}
}
Rect.recycle(rect);
}
}
for (const rect of this.freeRects) {
Rect.recycle(rect);
}
this.freeRects.length = 0;
this._count = 0;
this.rootRect = Rect.reuse(
this,
this.width + this.padding - this.border * 2,
this.height + this.padding - this.border * 2,
this.border,
this.border,
);
this.pushFreeRect(this.rootRect)
}
/**
*
*/
destroy() {
this.reset();
this._texture.destroy();
}
}

View File

@ -355,6 +355,34 @@ export default class TTFAssembler extends Assembler2D {
return this.packDynamicAtlasAndCheckMaterial(comp, frame); return this.packDynamicAtlasAndCheckMaterial(comp, frame);
} }
packDynamicAtlasAndCheckMaterial(comp, frame) {
const allowDynamicAtlas = comp.allowDynamicAtlas;
if ((cc.sp.allowDynamicAtlas && allowDynamicAtlas === 0) || allowDynamicAtlas === 1) {
frame._texture._uuid = _fontDesc
+ _overflow
+ (_premultiply ? 'P' : 'NP')
+ (_enableUnderline ? 'UL' : 'NUL')
+ _string;
if (_outlineComp) {
frame._texture._uuid += _outlineComp.color.toHEX()
+ ','
+ _outlineComp.width
+ ',';
}
if (_shadowComp) {
frame._texture._uuid += _shadowComp.color.toHEX()
+ _shadowComp.offset.x
+ ','
+ _shadowComp.offset.y
+ ','
+ _shadowComp.blur;
}
}
return super.packDynamicAtlasAndCheckMaterial(comp, frame);
}
_updateLabelDimensions () { _updateLabelDimensions () {
let maxTextureSize = cc.renderer.device.caps ? cc.renderer.device.caps.maxTextureSize : MAX_SIZE; let maxTextureSize = cc.renderer.device.caps ? cc.renderer.device.caps.maxTextureSize : MAX_SIZE;
if (_canvasSize.width > maxTextureSize || _canvasSize.height > maxTextureSize) { if (_canvasSize.width > maxTextureSize || _canvasSize.height > maxTextureSize) {