phaser/v3/merge/loader/LoaderParser.js
2016-11-23 00:17:46 +00:00

515 lines
19 KiB
JavaScript

/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* Phaser.LoaderParser parses data objects from Phaser.Loader that need more preparation before they can be inserted into the Cache.
*
* @class Phaser.LoaderParser
*/
Phaser.LoaderParser = {
/**
* Alias for xmlBitmapFont, for backwards compatibility.
*
* @method Phaser.LoaderParser.bitmapFont
* @param {object} xml - XML data you want to parse.
* @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
* @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
* @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
* @return {object} The parsed Bitmap Font data.
*/
bitmapFont: function (xml, baseTexture, xSpacing, ySpacing) {
return this.xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing);
},
/**
* Parse a Bitmap Font from an XML file.
*
* @method Phaser.LoaderParser.xmlBitmapFont
* @param {object} xml - XML data you want to parse.
* @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
* @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
* @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
* @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
* @return {object} The parsed Bitmap Font data.
*/
xmlBitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame) {
var data = {};
var info = xml.getElementsByTagName('info')[0];
var common = xml.getElementsByTagName('common')[0];
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) + ySpacing;
data.chars = {};
var letters = xml.getElementsByTagName('char');
var x = (frame) ? frame.x : 0;
var y = (frame) ? frame.y : 0;
for (var i = 0; i < letters.length; i++)
{
var charCode = parseInt(letters[i].getAttribute('id'), 10);
data.chars[charCode] = {
x: x + parseInt(letters[i].getAttribute('x'), 10),
y: y + parseInt(letters[i].getAttribute('y'), 10),
width: parseInt(letters[i].getAttribute('width'), 10),
height: parseInt(letters[i].getAttribute('height'), 10),
xOffset: parseInt(letters[i].getAttribute('xoffset'), 10),
yOffset: parseInt(letters[i].getAttribute('yoffset'), 10),
xAdvance: parseInt(letters[i].getAttribute('xadvance'), 10) + xSpacing,
kerning: {}
};
}
var kernings = xml.getElementsByTagName('kerning');
for (i = 0; i < kernings.length; i++)
{
var first = parseInt(kernings[i].getAttribute('first'), 10);
var second = parseInt(kernings[i].getAttribute('second'), 10);
var amount = parseInt(kernings[i].getAttribute('amount'), 10);
data.chars[second].kerning[first] = amount;
}
return this.finalizeBitmapFont(baseTexture, data);
},
/**
* Parse a Bitmap Font from a JSON file.
*
* @method Phaser.LoaderParser.jsonBitmapFont
* @param {object} json - JSON data you want to parse.
* @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
* @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
* @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
* @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
* @return {object} The parsed Bitmap Font data.
*/
jsonBitmapFont: function (json, baseTexture, xSpacing, ySpacing, frame) {
var data = {
font: json.font.info._face,
size: parseInt(json.font.info._size, 10),
lineHeight: parseInt(json.font.common._lineHeight, 10) + ySpacing,
chars: {}
};
var x = (frame) ? frame.x : 0;
var y = (frame) ? frame.y : 0;
json.font.chars["char"].forEach(
function parseChar(letter) {
var charCode = parseInt(letter._id, 10);
data.chars[charCode] = {
x: x + parseInt(letter._x, 10),
y: y + parseInt(letter._y, 10),
width: parseInt(letter._width, 10),
height: parseInt(letter._height, 10),
xOffset: parseInt(letter._xoffset, 10),
yOffset: parseInt(letter._yoffset, 10),
xAdvance: parseInt(letter._xadvance, 10) + xSpacing,
kerning: {}
};
}
);
if (json.font.kernings && json.font.kernings.kerning)
{
json.font.kernings.kerning.forEach(
function parseKerning(kerning) {
data.chars[kerning._second].kerning[kerning._first] = parseInt(kerning._amount, 10);
}
);
}
return this.finalizeBitmapFont(baseTexture, data);
},
/**
* Finalize Bitmap Font parsing.
*
* @method Phaser.LoaderParser.finalizeBitmapFont
* @private
* @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
* @param {object} bitmapFontData - Pre-parsed bitmap font data.
* @return {object} The parsed Bitmap Font data.
*/
finalizeBitmapFont: function (baseTexture, bitmapFontData) {
Object.keys(bitmapFontData.chars).forEach(
function addTexture(charCode) {
var letter = bitmapFontData.chars[charCode];
letter.texture = new PIXI.Texture(baseTexture, new Phaser.Rectangle(letter.x, letter.y, letter.width, letter.height));
}
);
return bitmapFontData;
},
/**
* Extract PVR header from loaded binary
*
* @method Phaser.LoaderParser.pvr
* @param {ArrayBuffer} arrayBuffer
* @return {object} The parsed PVR file including texture data.
*/
pvr: function (arrayBuffer) {
// Reference: http://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
// PVR 3 header structure
// ---------------------------------------
// address: 0, size: 4 bytes version
// address: 4, size: 4 bytes flags
// address: 8, size: 8 bytes pixel format
// address: 16, size: 4 bytes color space
// address: 20, size: 4 bytes channel type
// address: 24, size: 4 bytes height
// address: 28, size: 4 bytes width
// address: 32, size: 4 bytes depth
// address: 36, size: 4 bytes number of surfaces
// address: 40, size: 4 bytes number of faces
// address: 44, size: 4 bytes number of mipmaps
// address: 48, size: 4 bytes meta data size
// ---------------------------------------
var uintArray = new Uint32Array(arrayBuffer.slice(0, 52)),
byteArray = new Uint8Array(arrayBuffer),
pvrHeader = null,
pixelFormat = (uintArray[3] << 32 | uintArray[2]),
compressionAlgorithm,
glExtensionFormat = 0;
if (uintArray[0] === 0x03525650 &&
[ // Validate WebGL Pixel Format
0, 1, 2, 3,
6, 7, 9, 11
].indexOf(pixelFormat) >= 0
) {
if (pixelFormat >= 0 && pixelFormat <= 3) {
compressionAlgorithm = 'PVRTC';
} else if (pixelFormat >= 7 && pixelFormat <= 11) {
compressionAlgorithm = 'S3TC';
} else if (pixelFormat === 6) {
compressionAlgorithm = 'ETC1';
}
switch (pixelFormat) {
case 0:
glExtensionFormat = 0x8C01;
break;
case 1:
glExtensionFormat = 0x8C03;
break;
case 2:
glExtensionFormat = 0x8C00;
break;
case 3:
glExtensionFormat = 0x8C02;
break;
case 6:
glExtensionFormat = 0x8D64;
break;
case 7:
glExtensionFormat = 0x83F1;
break;
case 9:
glExtensionFormat = 0x83F2;
break;
case 11:
glExtensionFormat = 0x83F3;
break;
default:
glExtensionFormat = -1;
}
pvrHeader = {
complete: true,
fileFormat: 'PVR',
compressionAlgorithm: compressionAlgorithm,
flags: uintArray[1],
pixelFormat: pixelFormat,
colorSpace: uintArray[4],
channelType: uintArray[5],
height: uintArray[6],
width: uintArray[7],
depth: uintArray[8],
numberOfSurfaces: uintArray[9],
numberOfFaces: uintArray[10],
numberOfMipmaps: uintArray[11],
metaDataSize: uintArray[12],
textureData: byteArray.subarray(52 + uintArray[12], byteArray.byteLength),
glExtensionFormat: glExtensionFormat
};
}
return pvrHeader;
},
/**
* Extract DDS header from loaded binary
*
* @method Phaser.LoaderParser.dds
* @param {ArrayBuffer} arrayBuffer
* @return {object} The parsed DDS file including texture data.
*/
dds: function (arrayBuffer) {
// Reference at: https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx
// DDS header structure
// ---------------------------------------
// address: 0, size: 4 bytes Identifier 'DDS '
// address: 4, size: 4 bytes size
// address: 8, size: 4 bytes flags
// address: 12, size: 4 bytes height
// address: 16, size: 4 bytes width
// address: 20, size: 4 bytes pitch or linear size
// address: 24, size: 4 bytes depth
// address: 28, size: 4 bytes mipmap count
// address: 32, size: 44 bytes reserved space 1
// address: 76, size: 4 pixel format size
// address: 80, size: 4 pixel format flag
// address: 84, size: 4 pixel format four CC
// address: 88, size: 4 pixel format bit count
// address: 92, size: 4 pixel format R bit mask
// address: 96, size: 4 pixel format G bit mask
// address: 100, size: 4 pixel format B bit mask
// address: 104, size: 4 pixel format A bit mask
// address: 108, size: 4 caps 1
// address: 112, size: 4 caps 2
// address: 116, size: 4 caps 3
// address: 120, size: 4 caps 4
// address: 124, size: 4 reserved 2
// -- DXT10 extension
// address: 130, size: 4 DXGI Format
// address: 134, size: 4 resource dimension
// address: 138, size: 4 misc flag
// address: 142, size: 4 array size
// address: 146, size: 4 misc flag 2
// ---------------------------------------
var byteArray = new Uint8Array(arrayBuffer),
uintArray = new Uint32Array(arrayBuffer),
ddsHeader = null;
if (byteArray[0] === 0x44 &&
byteArray[1] === 0x44 &&
byteArray[2] === 0x53 &&
byteArray[3] === 0x20) {
ddsHeader = {
complete: true,
fileFormat: 'DDS',
compressionAlgorithm: 'S3TC',
size: uintArray[1],
flags: uintArray[2],
height: uintArray[3],
width: uintArray[4],
pitch: uintArray[5],
depth: uintArray[6],
mipmapCount: uintArray[7],
formatSize: uintArray[19],
formatFlag: uintArray[19],
formatFourCC: [
String.fromCharCode(byteArray[84]),
String.fromCharCode(byteArray[85]),
String.fromCharCode(byteArray[86]),
String.fromCharCode(byteArray[87])
].join(''),
formatBitCount: uintArray[21],
formatRBitMask: uintArray[22],
formatGBitMask: uintArray[23],
formatBBitMask: uintArray[24],
formatABitMask: uintArray[25],
caps1: uintArray[26],
caps2: uintArray[27],
caps3: uintArray[28],
caps4: uintArray[29],
reserved2: uintArray[30],
DXGIFormat: null,
resourceDimension: null,
miscFlag: null,
arraySize: null,
textureData: byteArray.subarray(uintArray[1] + 4, byteArray.byteLength)
};
if (ddsHeader.formatFourCC === 'DX10') {
ddsHeader.DXGIFormat = uintArray[31];
ddsHeader.resourceDimension = uintArray[32];
ddsHeader.miscFlag = uintArray[33];
ddsHeader.arraySize = uintArray[34];
ddsHeader.miscFlag = uintArray[35];
}
}
return ddsHeader;
},
/**
* Extract KTX header from loaded binary
*
* @method Phaser.LoaderParser.ktx
* @param {ArrayBuffer} arrayBuffer
* @return {object} The parsed KTX file including texture data.
*/
ktx: function (arrayBuffer) {
// Reference: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// KTX header structure
// ---------------------------------------
// address: 0, size 12 bytes: Identifier '«KTX 11»\r\n\x1A\n'
// address: 12, size 4 bytes: endianness
// address: 16, size 4 bytes: GL type
// address: 20, size 4 bytes: GL type size
// address: 24, size 4 bytes: GL format
// address: 28, size 4 bytes: GL internal format
// address: 32, size 4 bytes: GL base internal format
// address: 36, size 4 bytes: pixel width
// address: 40, size 4 bytes: pixel height
// address: 44, size 4 bytes: pixel depth
// address: 48, size 4 bytes: number of array elements
// address: 52, size 4 bytes: number of faces
// address: 56, size 4 bytes: number of mipmap levels
// address: 60, size 4 bytes: bytes of key value data
// address: 64, size 4 bytes: key and value bytes size
// address: X, size 1 byte : key and value
// address: X, size 1 byte : value padding
// address: X, size 4 byte : image size
// ---------------------------------------
var byteArray = new Uint8Array(arrayBuffer),
uintArray = new Uint32Array(arrayBuffer),
ktxHeader = null,
imageSizeOffset = 16 + (uintArray[15] / 4) | 0,
imageSize = uintArray[imageSizeOffset],
glInternalFormat = uintArray[7],
compressionAlgorithm = 0;
if (byteArray[0] === 0xAB && byteArray[1] === 0x4B &&
byteArray[2] === 0x54 && byteArray[3] === 0x58 &&
byteArray[4] === 0x20 && byteArray[5] === 0x31 &&
byteArray[6] === 0x31 && byteArray[7] === 0xBB &&
byteArray[8] === 0x0D && byteArray[9] === 0x0A &&
byteArray[10] === 0x1A && byteArray[11] === 0x0A &&
//Check if internal GL format is supported by WebGL
[
// ETC1
0x8D64,
// PVRTC
0x8C00, 0x8C01, 0x8C02, 0x8C03,
// DXTC | S3TC
0x83F0, 0x83F1, 0x83F2, 0x83F3
].indexOf(glInternalFormat) >= 0) {
switch (glInternalFormat) {
case 0x8D64:
compressionAlgorithm = 'ETC1';
break;
case 0x8C00:
case 0x8C01:
case 0x8C02:
case 0x8C03:
compressionAlgorithm = 'PVRTC';
break;
case 0x83F0:
case 0x83F1:
case 0x83F2:
case 0x83F3:
compressionAlgorithm = 'S3TC';
break;
}
ktxHeader = {
complete: true,
fileFormat: 'KTX',
compressionAlgorithm: compressionAlgorithm,
endianness: uintArray[3],
glType: uintArray[4],
glTypeSize: uintArray[5],
glFormat: uintArray[6],
glInternalFormat: uintArray[7],
glBaseInternalFormat: uintArray[8],
width: uintArray[9],
height: uintArray[10],
pixelDepth: uintArray[11],
numberOfArrayElements: uintArray[12],
numberOfFaces: uintArray[13],
numberOfMipmapLevels: uintArray[14],
bytesOfKeyValueData: uintArray[15],
keyAndValueByteSize: uintArray[16],
imageSize: imageSize,
textureData: byteArray.subarray((imageSizeOffset + 1) * 4, imageSize + 100)
};
}
return ktxHeader;
},
/**
* Extract PKM header from loaded binary
*
* @method Phaser.LoaderParser.pkm
* @param {ArrayBuffer} arrayBuffer
* @return {object} The parsed PKM file including texture data.
*/
pkm: function (arrayBuffer) {
// PKM header structure
// ---------------------------------------
// address: 0, size 4 bytes: for 'PKM '
// address: 4, size 2 bytes: for version
// address: 6, size 2 bytes: for type
// address: 8, size 2 bytes: for extended width
// address: 10, size 2 bytes: for extended height
// address: 12, size 2 bytes: for original width
// address: 14, size 2 bytes: for original height
// address: 16, texture data
// ---------------------------------------
var byteArray = new Uint8Array(arrayBuffer),
pkmHeader = null;
if (byteArray[0] === 0x50 &&
byteArray[1] === 0x4B &&
byteArray[2] === 0x4D &&
byteArray[3] === 0x20) {
pkmHeader = {
complete: true,
fileFormat: 'PKM',
compressionAlgorithm: 'ETC1',
format: ((byteArray[6] << 8 | byteArray[7])) & 0xFFFF,
width: ((byteArray[8] << 8 | byteArray[9])) & 0xFFFF,
height: ((byteArray[10] << 8 | byteArray[11])) & 0xFFFF,
originalWidth: ((byteArray[12] << 8 | byteArray[13])) & 0xFFFF,
originalHeight: ((byteArray[14] << 8 | byteArray[15])) & 0xFFFF,
textureData: byteArray.subarray(16, byteArray.length)
};
}
return pkmHeader;
}
};