dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
468 lines (407 loc) • 21.4 kB
text/typescript
/**
* The MIT License (MIT)
*
* Copyright (c) 2012-2017 DragonBones team and other contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace dragonBones {
/**
* @internal
* @private
*/
export class BinaryDataParser extends ObjectDataParser {
private _binaryOffset: number;
private _binary: ArrayBuffer;
private _intArrayBuffer: Int16Array;
private _floatArrayBuffer: Float32Array;
private _frameIntArrayBuffer: Int16Array;
private _frameFloatArrayBuffer: Float32Array;
private _frameArrayBuffer: Int16Array;
private _timelineArrayBuffer: Uint16Array;
private _inRange(a: number, min: number, max: number): boolean {
return min <= a && a <= max;
}
private _decodeUTF8(data: Uint8Array): string {
const EOF_byte = -1;
const EOF_code_point = -1;
const FATAL_POINT = 0xFFFD;
let pos = 0;
let result = "";
let code_point;
let utf8_code_point = 0;
let utf8_bytes_needed = 0;
let utf8_bytes_seen = 0;
let utf8_lower_boundary = 0;
while (data.length > pos) {
let _byte = data[pos++];
if (_byte === EOF_byte) {
if (utf8_bytes_needed !== 0) {
code_point = FATAL_POINT;
}
else {
code_point = EOF_code_point;
}
}
else {
if (utf8_bytes_needed === 0) {
if (this._inRange(_byte, 0x00, 0x7F)) {
code_point = _byte;
}
else {
if (this._inRange(_byte, 0xC2, 0xDF)) {
utf8_bytes_needed = 1;
utf8_lower_boundary = 0x80;
utf8_code_point = _byte - 0xC0;
}
else if (this._inRange(_byte, 0xE0, 0xEF)) {
utf8_bytes_needed = 2;
utf8_lower_boundary = 0x800;
utf8_code_point = _byte - 0xE0;
}
else if (this._inRange(_byte, 0xF0, 0xF4)) {
utf8_bytes_needed = 3;
utf8_lower_boundary = 0x10000;
utf8_code_point = _byte - 0xF0;
}
else {
}
utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
code_point = null;
}
}
else if (!this._inRange(_byte, 0x80, 0xBF)) {
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
pos--;
code_point = _byte;
}
else {
utf8_bytes_seen += 1;
utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
if (utf8_bytes_seen !== utf8_bytes_needed) {
code_point = null;
}
else {
let cp = utf8_code_point;
let lower_boundary = utf8_lower_boundary;
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
if (this._inRange(cp, lower_boundary, 0x10FFFF) && !this._inRange(cp, 0xD800, 0xDFFF)) {
code_point = cp;
}
else {
code_point = _byte;
}
}
}
}
//Decode string
if (code_point !== null && code_point !== EOF_code_point) {
if (code_point <= 0xFFFF) {
if (code_point > 0) result += String.fromCharCode(code_point);
}
else {
code_point -= 0x10000;
result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
}
}
}
return result;
}
private _getUTF16Key(value: string): string {
for (let i = 0, l = value.length; i < l; ++i) {
if (value.charCodeAt(i) > 255) {
return encodeURI(value);
}
}
return value;
}
private _parseBinaryTimeline(type: TimelineType, offset: number, timelineData: TimelineData | null = null): TimelineData {
const timeline = timelineData !== null ? timelineData : BaseObject.borrowObject(TimelineData);
timeline.type = type;
timeline.offset = offset;
this._timeline = timeline;
const keyFrameCount = this._timelineArrayBuffer[timeline.offset + BinaryOffset.TimelineKeyFrameCount];
if (keyFrameCount === 1) {
timeline.frameIndicesOffset = -1;
}
else {
let frameIndicesOffset = 0;
const totalFrameCount = this._animation.frameCount + 1; // One more frame than animation.
const frameIndices = this._data.frameIndices;
if (DragonBones.webAssembly) {
frameIndicesOffset = (frameIndices as any).size();
(frameIndices as any).resize(frameIndicesOffset + totalFrameCount, 0);
}
else {
frameIndicesOffset = frameIndices.length;
frameIndices.length += totalFrameCount;
}
timeline.frameIndicesOffset = frameIndicesOffset;
for (
let i = 0, iK = 0, frameStart = 0, frameCount = 0;
i < totalFrameCount;
++i
) {
if (frameStart + frameCount <= i && iK < keyFrameCount) {
frameStart = this._frameArrayBuffer[this._animation.frameOffset + this._timelineArrayBuffer[timeline.offset + BinaryOffset.TimelineFrameOffset + iK]];
if (iK === keyFrameCount - 1) {
frameCount = this._animation.frameCount - frameStart;
}
else {
frameCount = this._frameArrayBuffer[this._animation.frameOffset + this._timelineArrayBuffer[timeline.offset + BinaryOffset.TimelineFrameOffset + iK + 1]] - frameStart;
}
iK++;
}
if (DragonBones.webAssembly) {
(frameIndices as any).set(frameIndicesOffset + i, iK - 1);
}
else {
frameIndices[frameIndicesOffset + i] = iK - 1;
}
}
}
this._timeline = null as any; //
return timeline;
}
protected _parseMesh(rawData: any, mesh: MeshDisplayData): void {
mesh.offset = rawData[DataParser.OFFSET];
const weightOffset = this._intArrayBuffer[mesh.offset + BinaryOffset.MeshWeightOffset];
if (weightOffset >= 0) {
const weight = BaseObject.borrowObject(WeightData);
const vertexCount = this._intArrayBuffer[mesh.offset + BinaryOffset.MeshVertexCount];
const boneCount = this._intArrayBuffer[weightOffset + BinaryOffset.WeigthBoneCount];
weight.offset = weightOffset;
for (let i = 0; i < boneCount; ++i) {
const boneIndex = this._intArrayBuffer[weightOffset + BinaryOffset.WeigthBoneIndices + i];
weight.addBone(this._rawBones[boneIndex]);
}
let boneIndicesOffset = weightOffset + BinaryOffset.WeigthBoneIndices + boneCount;
let weightCount = 0;
for (let i = 0, l = vertexCount; i < l; ++i) {
const vertexBoneCount = this._intArrayBuffer[boneIndicesOffset++];
weightCount += vertexBoneCount;
boneIndicesOffset += vertexBoneCount;
}
weight.count = weightCount;
mesh.weight = weight;
}
}
protected _parseAnimation(rawData: any): AnimationData {
const animation = BaseObject.borrowObject(AnimationData);
animation.frameCount = Math.max(ObjectDataParser._getNumber(rawData, DataParser.DURATION, 1), 1);
animation.playTimes = ObjectDataParser._getNumber(rawData, DataParser.PLAY_TIMES, 1);
animation.duration = animation.frameCount / this._armature.frameRate; // float
animation.fadeInTime = ObjectDataParser._getNumber(rawData, DataParser.FADE_IN_TIME, 0.0);
animation.scale = ObjectDataParser._getNumber(rawData, DataParser.SCALE, 1.0);
animation.name = ObjectDataParser._getString(rawData, DataParser.NAME, DataParser.DEFAULT_NAME);
if (animation.name.length === 0) {
animation.name = DataParser.DEFAULT_NAME;
}
// Offsets.
const offsets = rawData[DataParser.OFFSET] as Array<number>;
animation.frameIntOffset = offsets[0];
animation.frameFloatOffset = offsets[1];
animation.frameOffset = offsets[2];
this._animation = animation;
if (DataParser.ACTION in rawData) {
animation.actionTimeline = this._parseBinaryTimeline(TimelineType.Action, rawData[DataParser.ACTION]);
}
if (DataParser.Z_ORDER in rawData) {
animation.zOrderTimeline = this._parseBinaryTimeline(TimelineType.ZOrder, rawData[DataParser.Z_ORDER]);
}
if (DataParser.BONE in rawData) {
const rawTimeliness = rawData[DataParser.BONE];
for (let k in rawTimeliness) {
const rawTimelines = rawTimeliness[k] as Array<number>;
if (DragonBones.webAssembly) {
k = this._getUTF16Key(k);
}
const bone = this._armature.getBone(k);
if (bone === null) {
continue;
}
for (let i = 0, l = rawTimelines.length; i < l; i += 2) {
const timelineType = rawTimelines[i];
const timelineOffset = rawTimelines[i + 1];
const timeline = this._parseBinaryTimeline(timelineType, timelineOffset);
this._animation.addBoneTimeline(bone, timeline);
}
}
}
if (DataParser.SURFACE in rawData) {
const rawTimeliness = rawData[DataParser.SURFACE];
for (let k in rawTimeliness) {
const rawTimelines = rawTimeliness[k] as Array<number>;
if (DragonBones.webAssembly) {
k = this._getUTF16Key(k);
}
const surface = this._armature.getBone(k) as SurfaceData;
if (surface === null) {
continue;
}
for (let i = 0, l = rawTimelines.length; i < l; i += 2) {
const timelineType = rawTimelines[i];
const timelineOffset = rawTimelines[i + 1];
const timeline = this._parseBinaryTimeline(timelineType, timelineOffset);
this._animation.addSurfaceTimeline(surface, timeline);
}
}
}
if (DataParser.SLOT in rawData) {
const rawTimeliness = rawData[DataParser.SLOT];
for (let k in rawTimeliness) {
const rawTimelines = rawTimeliness[k] as Array<number>;
if (DragonBones.webAssembly) {
k = this._getUTF16Key(k);
}
const slot = this._armature.getSlot(k);
if (slot === null) {
continue;
}
for (let i = 0, l = rawTimelines.length; i < l; i += 2) {
const timelineType = rawTimelines[i];
const timelineOffset = rawTimelines[i + 1];
const timeline = this._parseBinaryTimeline(timelineType, timelineOffset);
this._animation.addSlotTimeline(slot, timeline);
}
}
}
if (DataParser.CONSTRAINT in rawData) {
const rawTimeliness = rawData[DataParser.CONSTRAINT];
for (let k in rawTimeliness) {
const rawTimelines = rawTimeliness[k] as Array<number>;
if (DragonBones.webAssembly) {
k = this._getUTF16Key(k);
}
const constraint = this._armature.getConstraint(k);
if (constraint === null) {
continue;
}
for (let i = 0, l = rawTimelines.length; i < l; i += 2) {
const timelineType = rawTimelines[i];
const timelineOffset = rawTimelines[i + 1];
const timeline = this._parseBinaryTimeline(timelineType, timelineOffset);
this._animation.addConstraintTimeline(constraint, timeline);
}
}
}
if (DataParser.ANIMATION in rawData) {
const rawTimeliness = rawData[DataParser.ANIMATION];
for (let k in rawTimeliness) {
const rawTimelines = rawTimeliness[k] as Array<number>;
if (DragonBones.webAssembly) {
k = this._getUTF16Key(k);
}
for (let i = 0, l = rawTimelines.length; i < l; i += 2) {
const timelineType = rawTimelines[i];
const timelineOffset = rawTimelines[i + 1];
const timeline = this._parseBinaryTimeline(timelineType, timelineOffset);
this._animation.addAnimationTimeline(k, timeline);
}
}
}
this._animation = null as any;
return animation;
}
protected _parseArray(rawData: any): void {
const offsets = rawData[DataParser.OFFSET] as Array<number>;
const l1 = offsets[1];
const l2 = offsets[3];
const l3 = offsets[5];
const l4 = offsets[7];
const l5 = offsets[9];
const l6 = offsets[11];
const intArray = new Int16Array(this._binary, this._binaryOffset + offsets[0], l1 / Int16Array.BYTES_PER_ELEMENT);
const floatArray = new Float32Array(this._binary, this._binaryOffset + offsets[2], l2 / Float32Array.BYTES_PER_ELEMENT);
const frameIntArray = new Int16Array(this._binary, this._binaryOffset + offsets[4], l3 / Int16Array.BYTES_PER_ELEMENT);
const frameFloatArray = new Float32Array(this._binary, this._binaryOffset + offsets[6], l4 / Float32Array.BYTES_PER_ELEMENT);
const frameArray = new Int16Array(this._binary, this._binaryOffset + offsets[8], l5 / Int16Array.BYTES_PER_ELEMENT);
const timelineArray = new Uint16Array(this._binary, this._binaryOffset + offsets[10], l6 / Uint16Array.BYTES_PER_ELEMENT);
if (DragonBones.webAssembly) {
const lTotal = l1 + l2 + l3 + l4 + l5 + l6;
const bufferPointer = webAssemblyModule._malloc(lTotal);
const rawArray = new Uint8Array(this._binary, this._binaryOffset, lTotal / Uint8Array.BYTES_PER_ELEMENT);
const copyArray = new Uint8Array(webAssemblyModule.HEAP16.buffer, bufferPointer, rawArray.length);
for (let i = 0, l = rawArray.length; i < l; ++i) {
copyArray[i] = rawArray[i];
}
webAssemblyModule.setDataBinary(this._data, bufferPointer, l1, l2, l3, l4, l5, l6);
this._intArrayBuffer = intArray;
this._floatArrayBuffer = floatArray;
this._frameIntArrayBuffer = frameIntArray;
this._frameFloatArrayBuffer = frameFloatArray;
this._frameArrayBuffer = frameArray;
this._timelineArrayBuffer = timelineArray;
}
else {
this._data.binary = this._binary;
this._data.intArray = this._intArrayBuffer = intArray;
this._data.floatArray = this._floatArrayBuffer = floatArray;
this._data.frameIntArray = this._frameIntArrayBuffer = frameIntArray;
this._data.frameFloatArray = this._frameFloatArrayBuffer = frameFloatArray;
this._data.frameArray = this._frameArrayBuffer = frameArray;
this._data.timelineArray = this._timelineArrayBuffer = timelineArray;
}
}
public parseDragonBonesData(rawData: any, scale: number = 1): DragonBonesData | null {
console.assert(rawData !== null && rawData !== undefined && rawData instanceof ArrayBuffer, "Data error.");
const tag = new Uint8Array(rawData, 0, 8);
if (
tag[0] !== "D".charCodeAt(0) ||
tag[1] !== "B".charCodeAt(0) ||
tag[2] !== "D".charCodeAt(0) ||
tag[3] !== "T".charCodeAt(0)
) {
console.assert(false, "Nonsupport data.");
return null;
}
const headerLength = new Uint32Array(rawData, 8, 1)[0];
const headerBytes = new Uint8Array(rawData, 8 + 4, headerLength);
const headerString = this._decodeUTF8(headerBytes);
const header = JSON.parse(headerString);
//
this._binaryOffset = 8 + 4 + headerLength;
this._binary = rawData;
return super.parseDragonBonesData(header, scale);
}
private static _binaryDataParserInstance: BinaryDataParser = null as any;
/**
* - Deprecated, please refer to {@link dragonBones.BaseFactory#parseDragonBonesData()}.
* @deprecated
* @language en_US
*/
/**
* - 已废弃,请参考 {@link dragonBones.BaseFactory#parseDragonBonesData()}。
* @deprecated
* @language zh_CN
*/
public static getInstance(): BinaryDataParser {
if (BinaryDataParser._binaryDataParserInstance === null) {
BinaryDataParser._binaryDataParserInstance = new BinaryDataParser();
}
return BinaryDataParser._binaryDataParserInstance;
}
}
}