UNPKG

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
/** * @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; } };