Files
esengine/packages/rendering/fairygui/src/utils/ByteBuffer.ts

396 lines
9.4 KiB
TypeScript
Raw Normal View History

feat(fairygui): FairyGUI 完整集成 (#314) * feat(fairygui): FairyGUI ECS 集成核心架构 实现 FairyGUI 的 ECS 原生集成,完全替代旧 UI 系统: 核心类: - GObject: UI 对象基类,支持变换、可见性、关联、齿轮 - GComponent: 容器组件,管理子对象和控制器 - GRoot: 根容器,管理焦点、弹窗、输入分发 - GGroup: 组容器,支持水平/垂直布局 抽象层: - DisplayObject: 显示对象基类 - EventDispatcher: 事件分发 - Timer: 计时器 - Stage: 舞台,管理输入和缩放 布局系统: - Relations: 约束关联管理 - RelationItem: 24 种关联类型 基础设施: - Controller: 状态控制器 - Transition: 过渡动画 - ScrollPane: 滚动面板 - UIPackage: 包管理 - ByteBuffer: 二进制解析 * refactor(ui): 删除旧 UI 系统,使用 FairyGUI 替代 * feat(fairygui): 实现 UI 控件 - 添加显示类:Image、TextField、Graph - 添加基础控件:GImage、GTextField、GGraph - 添加交互控件:GButton、GProgressBar、GSlider - 更新 IRenderCollector 支持 Graph 渲染 - 扩展 Controller 添加 selectedPageId - 添加 STATE_CHANGED 事件类型 * feat(fairygui): 现代化架构重构 - 增强 EventDispatcher 支持类型安全、优先级和传播控制 - 添加 PropertyBinding 响应式属性绑定系统 - 添加 ServiceContainer 依赖注入容器 - 添加 UIConfig 全局配置系统 - 添加 UIObjectFactory 对象工厂 - 实现 RenderBridge 渲染桥接层 - 实现 Canvas2DBackend 作为默认渲染后端 - 扩展 IRenderCollector 支持更多图元类型 * feat(fairygui): 九宫格渲染和资源加载修复 - 修复 FGUIUpdateSystem 支持路径和 GUID 两种加载方式 - 修复 GTextInput 同时设置 _displayObject 和 _textField - 实现九宫格渲染展开为 9 个子图元 - 添加 sourceWidth/sourceHeight 用于九宫格计算 - 添加 DOMTextRenderer 文本渲染层(临时方案) * fix(fairygui): 修复 GGraph 颜色读取 * feat(fairygui): 虚拟节点 Inspector 和文本渲染支持 * fix(fairygui): 编辑器状态刷新和遗留引用修复 - 修复切换 FGUI 包后组件列表未刷新问题 - 修复切换组件后 viewport 未清理旧内容问题 - 修复虚拟节点在包加载后未刷新问题 - 重构为事件驱动架构,移除轮询机制 - 修复 @esengine/ui 遗留引用,统一使用 @esengine/fairygui * fix: 移除 tsconfig 中的 @esengine/ui 引用
2025-12-22 10:52:54 +08:00
/**
* ByteBuffer
*
* Binary data reader for parsing FairyGUI package files.
*
* FairyGUI
*/
export class ByteBuffer {
private _data: DataView;
private _position: number = 0;
private _littleEndian: boolean = false;
private _stringTable: string[] = [];
private _version: number = 0;
constructor(buffer: ArrayBuffer, offset: number = 0, length?: number) {
length = length ?? buffer.byteLength - offset;
this._data = new DataView(buffer, offset, length);
}
/**
* Get buffer length
*
*/
public get length(): number {
return this._data.byteLength;
}
/**
* Get current position
*
*/
public get position(): number {
return this._position;
}
/**
* Set current position
*
*/
public set position(value: number) {
this._position = value;
}
/**
* Get version
*
*/
public get version(): number {
return this._version;
}
/**
* Set version
*
*/
public set version(value: number) {
this._version = value;
}
/**
* Check if can read more bytes
*
*/
public get bytesAvailable(): number {
return this._data.byteLength - this._position;
}
/**
* Skip bytes
*
*/
public skip(count: number): void {
this._position += count;
}
/**
* Seek to position
*
*/
public seek(indexTablePos: number, blockIndex: number): boolean {
const tmp = this._position;
this._position = indexTablePos;
const segCount = this.getUint8();
if (blockIndex < segCount) {
const useShort = this.getUint8() === 1;
let newPos: number;
if (useShort) {
this._position = indexTablePos + 2 + 2 * blockIndex;
newPos = this.getUint16();
} else {
this._position = indexTablePos + 2 + 4 * blockIndex;
newPos = this.getUint32();
}
if (newPos > 0) {
this._position = indexTablePos + newPos;
return true;
} else {
this._position = tmp;
return false;
}
} else {
this._position = tmp;
return false;
}
}
// Read methods | 读取方法
public getUint8(): number {
const value = this._data.getUint8(this._position);
this._position += 1;
return value;
}
public getInt8(): number {
const value = this._data.getInt8(this._position);
this._position += 1;
return value;
}
public getUint16(): number {
const value = this._data.getUint16(this._position, this._littleEndian);
this._position += 2;
return value;
}
public getInt16(): number {
const value = this._data.getInt16(this._position, this._littleEndian);
this._position += 2;
return value;
}
public getUint32(): number {
const value = this._data.getUint32(this._position, this._littleEndian);
this._position += 4;
return value;
}
public getInt32(): number {
const value = this._data.getInt32(this._position, this._littleEndian);
this._position += 4;
return value;
}
public getFloat32(): number {
const value = this._data.getFloat32(this._position, this._littleEndian);
this._position += 4;
return value;
}
public getFloat64(): number {
const value = this._data.getFloat64(this._position, this._littleEndian);
this._position += 8;
return value;
}
/**
* Read boolean
*
*/
public readBool(): boolean {
return this.getUint8() === 1;
}
/**
* Read byte
*
*/
public readByte(): number {
return this.getUint8();
}
/**
* Read short
*
*/
public readShort(): number {
return this.getInt16();
}
/**
* Read unsigned short
*
*/
public readUshort(): number {
return this.getUint16();
}
/**
* Read int
*
*/
public readInt(): number {
return this.getInt32();
}
/**
* Read unsigned int
*
*/
public readUint(): number {
return this.getUint32();
}
/**
* Read float
*
*/
public readFloat(): number {
return this.getFloat32();
}
/**
* Read string from string table
*
*/
public readS(): string {
const index = this.getUint16();
if (index === 65535) {
return '';
}
return this._stringTable[index] || '';
}
/**
* Read string with length prefix
*
*/
public readString(): string {
const len = this.getUint16();
if (len === 0) {
return '';
}
return this.readStringWithLength(len);
}
private readStringWithLength(len: number): string {
const bytes = new Uint8Array(this._data.buffer, this._data.byteOffset + this._position, len);
this._position += len;
return new TextDecoder('utf-8').decode(bytes);
}
/**
* Read color as packed u32 (0xRRGGBBAA format)
* u320xRRGGBBAA
*/
public readColor(bHasAlpha: boolean = false): number {
const r = this.getUint8();
const g = this.getUint8();
const b = this.getUint8();
const a = this.getUint8();
return ((r << 24) | (g << 16) | (b << 8) | (bHasAlpha ? a : 0xFF)) >>> 0;
}
/**
* Read color as CSS string (always reads 4 bytes: r, g, b, a)
* CSS 4 r, g, b, a
*
* FairyGUI R, G, B, A
*/
public readColorS(bHasAlpha: boolean = false): string {
const byte0 = this.getUint8();
const byte1 = this.getUint8();
const byte2 = this.getUint8();
const byte3 = this.getUint8();
// FairyGUI stores colors as R, G, B, A
const r = byte0;
const g = byte1;
const b = byte2;
const a = byte3;
if (bHasAlpha && a !== 255) {
return `rgba(${r},${g},${b},${(a / 255).toFixed(2)})`;
} else {
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
}
/**
* Read bytes
*
*/
public readBytes(length: number): Uint8Array {
const bytes = new Uint8Array(this._data.buffer, this._data.byteOffset + this._position, length);
this._position += length;
return bytes;
}
/**
* Set string table
*
*/
public set stringTable(value: string[]) {
this._stringTable = value;
}
/**
* Get string table
*
*/
public get stringTable(): string[] {
return this._stringTable;
}
/**
* Alias for position getter
* position getter
*/
public get pos(): number {
return this._position;
}
/**
* Alias for position setter
* position setter
*/
public set pos(value: number) {
this._position = value;
}
/**
* Get underlying buffer
*
*/
public get buffer(): ArrayBuffer {
return this._data.buffer as ArrayBuffer;
}
/**
* Read UTF string (length-prefixed)
* UTF
*/
public readUTFString(): string {
const len = this.getUint16();
if (len === 0) {
return '';
}
return this.readStringWithLength(len);
}
/**
* Read string array
*
*/
public readSArray(count: number): string[] {
const arr: string[] = [];
for (let i = 0; i < count; i++) {
arr.push(this.readS());
}
return arr;
}
/**
* Read custom string with specified length
*
*/
public getCustomString(len: number): string {
const bytes = new Uint8Array(this._data.buffer, this._data.byteOffset + this._position, len);
this._position += len;
return new TextDecoder('utf-8').decode(bytes);
}
/**
* Read sub-buffer
*
*/
public readBuffer(): ByteBuffer {
const len = this.getUint32();
const buffer = new ByteBuffer(this._data.buffer as ArrayBuffer, this._data.byteOffset + this._position, len);
buffer.version = this._version;
buffer.stringTable = this._stringTable;
this._position += len;
return buffer;
}
/**
* Read Int32 (alias)
* Int32
*/
public readInt32(): number {
return this.getInt32();
}
/**
* Read Uint16 (alias)
* Uint16
*/
public readUint16(): number {
return this.getUint16();
}
}