phaser-ce
Version:
Phaser CE (Community Edition) is a fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.
610 lines (561 loc) • 23.3 kB
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.
* @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
* @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
* @return {object} The parsed Bitmap Font data.
*/
bitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame, resolution)
{
return this.xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing, frame, resolution);
},
/**
* 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.
* @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
* @return {object} The parsed Bitmap Font data.
*/
xmlBitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame, resolution)
{
if (resolution == null)
{
resolution = 1;
}
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);
var char = data.chars[charCode] = {
x: parseInt(letters[i].getAttribute('x'), 10),
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) / resolution,
yOffset: parseInt(letters[i].getAttribute('yoffset'), 10) / resolution,
xAdvance: (parseInt(letters[i].getAttribute('xadvance'), 10) + xSpacing) / resolution,
kerning: {}
};
if (frame && frame.trimmed)
{
if (char.x + char.width > frame.spriteSourceSizeX + frame.spriteSourceSizeW)
{
char.width -= char.x + char.width - frame.spriteSourceSizeX - frame.spriteSourceSizeW;
}
if (char.y + char.height > frame.spriteSourceSizeY + frame.spriteSourceSizeH)
{
char.height -= char.y + char.height - frame.spriteSourceSizeY - frame.spriteSourceSizeH;
}
if (char.x < frame.spriteSourceSizeX)
{
var diff = frame.spriteSourceSizeX - char.x;
char.x = 0;
char.width -= diff;
char.xOffset += diff;
}
if (char.y < frame.spriteSourceSizeY)
{
var diff = frame.spriteSourceSizeY - char.y;
char.y = 0;
char.height -= diff;
char.yOffset += diff;
}
}
char.x += x;
char.y += y;
}
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) / resolution;
if (data.chars[second])
{
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.
* @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
* @return {object} The parsed Bitmap Font data.
*/
jsonBitmapFont: function (json, baseTexture, xSpacing, ySpacing, frame, resolution)
{
if (resolution == null)
{
resolution = 1;
}
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);
var char = data.chars[charCode] = {
x: parseInt(letter._x, 10),
y: parseInt(letter._y, 10),
width: parseInt(letter._width, 10),
height: parseInt(letter._height, 10),
xOffset: parseInt(letter._xoffset, 10) / resolution,
yOffset: parseInt(letter._yoffset, 10) / resolution,
xAdvance: (parseInt(letter._xadvance, 10) + xSpacing) / resolution,
kerning: {}
};
if (frame && frame.trimmed)
{
if (char.x + char.width > frame.spriteSourceSizeX + frame.spriteSourceSizeW)
{
char.width -= char.x + char.width - frame.spriteSourceSizeX - frame.spriteSourceSizeW;
}
if (char.y + char.height > frame.spriteSourceSizeY + frame.spriteSourceSizeH)
{
char.height -= char.y + char.height - frame.spriteSourceSizeY - frame.spriteSourceSizeH;
}
if (char.x < frame.spriteSourceSizeX)
{
var diff = frame.spriteSourceSizeX - char.x;
char.x = 0;
char.width -= diff;
char.xOffset += diff;
}
if (char.y < frame.spriteSourceSizeY)
{
var diff = frame.spriteSourceSizeY - char.y;
char.y = 0;
char.height -= diff;
char.yOffset += diff;
}
}
char.x += x;
char.y += y;
}
);
if (json.font.kernings && json.font.kernings.kerning)
{
json.font.kernings.kerning.forEach(
function parseKerning (kerning)
{
if (data.chars[kerning._second])
{
data.chars[kerning._second].kerning[kerning._first] = parseInt(kerning._amount, 10) / resolution;
}
}
);
}
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];
}
else if(ddsHeader.formatFourCC === 'DXT5')
{
ddsHeader.glExtensionFormat = 0x83F3;
}
else if(ddsHeader.formatFourCC === 'DXT3')
{
ddsHeader.glExtensionFormat = 0x83F2;
}
}
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;
}
};