Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for all BMFont output file types in FlxBitmapFont #2949

Merged
merged 32 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5de3ccc
Added support for all bmfont file types (xml, text, binary)
UncertainProd Oct 29, 2023
c3690ef
Update FlxAngelCodeXmlAsset to also accept Bytes
UncertainProd Oct 29, 2023
a59ffb2
Cleanup of the `guessType` function and renamed FlxAngelCodeXmlAsset …
UncertainProd Oct 29, 2023
02f8c4f
Codeclimate fixes
UncertainProd Oct 29, 2023
2df42f6
Merge branch 'dev' of https://github.com/UncertainProd/flixel into bm…
UncertainProd Nov 8, 2023
9d7b3f9
Merge branch 'dev' of https://github.com/UncertainProd/flixel into bm…
UncertainProd Nov 9, 2023
ee2b299
Merge branch 'dev' of https://github.com/UncertainProd/flixel into bm…
UncertainProd Nov 17, 2023
9cfd2bb
Unified the bmfont parsing functions into one function
UncertainProd Nov 20, 2023
d5b8bc5
Merge branch 'dev' of https://github.com/UncertainProd/flixel into bm…
UncertainProd Nov 25, 2023
c0d99f1
Merge branch 'dev' into bmfont-parsing
Geokureli Dec 4, 2023
56f79af
fix codeclimate
Geokureli Dec 4, 2023
32ea881
Removed null types
UncertainProd Dec 7, 2023
0aac30e
crete abstracts over typedefs, move xml parsing
Geokureli Dec 7, 2023
021c12c
move text parsing to abstracts
Geokureli Dec 7, 2023
304534c
rename package
Geokureli Dec 7, 2023
f97715f
rename data classes
Geokureli Dec 7, 2023
e19e8cc
move bytes parsing to abstracts
Geokureli Dec 8, 2023
cfbd2ee
move to separate modules
Geokureli Dec 8, 2023
eb1d8f0
separate fields for letter and id
Geokureli Dec 8, 2023
4894bfc
add doc
Geokureli Dec 8, 2023
f1fedcc
remove redundnat local fields
Geokureli Dec 14, 2023
fe1047c
change field order and add padding/spacing type
Geokureli Dec 14, 2023
fb87542
new regex for bmfont attribute matching. start of test cases
MondayHopscotch Dec 14, 2023
d5dead2
better key/value attribute parser
Geokureli Dec 14, 2023
b5ef70e
add spacing/padding fromBytes()
Geokureli Dec 14, 2023
f1a3b86
Merge branch 'bmfont-parsing' of github.com:UncertainProd/flixel into…
MondayHopscotch Dec 14, 2023
04c8c99
fix ci
Geokureli Dec 14, 2023
0e1a9c5
remove unused function
Geokureli Dec 14, 2023
90e6966
add BMFont text format test
MondayHopscotch Dec 14, 2023
62db5d3
update BMFontTest to include binary file test
MondayHopscotch Dec 15, 2023
396131f
formatting
MondayHopscotch Dec 15, 2023
a3b9b2f
Merge branch 'bmfont-monday' into bmfont-parsing
Geokureli Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 34 additions & 54 deletions flixel/graphics/frames/FlxBitmapFont.hx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package flixel.graphics.frames;

import openfl.display.BitmapData;
import openfl.geom.Point;
import openfl.geom.Rectangle;
import flixel.FlxG;
import flixel.graphics.FlxGraphic;
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
import flixel.graphics.frames.FlxFramesCollection.FlxFrameCollectionType;
import flixel.graphics.frames.bmfontutils.BMFontFileTypeHelper;
import flixel.graphics.frames.bmfontutils.FlxBMFontParser;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxAssets;
import flixel.util.FlxColor;
import openfl.Assets;
import haxe.xml.Access;
import openfl.Assets;
import openfl.display.BitmapData;
import openfl.geom.Point;
import openfl.geom.Rectangle;

using flixel.util.FlxUnicodeUtil;

Expand Down Expand Up @@ -161,7 +163,7 @@ class FlxBitmapFont extends FlxFramesCollection
* @param data Font data.
* @return Generated bitmap font object.
*/
public static function fromAngelCode(source:FlxBitmapFontGraphicAsset, data:FlxAngelCodeXmlAsset):FlxBitmapFont
public static function fromAngelCode(source:FlxBitmapFontGraphicAsset, data:FlxAngelCodeAsset):FlxBitmapFont
{
var graphic:FlxGraphic = null;
var frame:FlxFrame = null;
Expand All @@ -183,79 +185,57 @@ class FlxBitmapFont extends FlxFramesCollection

font = new FlxBitmapFont(frame);

final fast = new Access(data.getXml().firstElement());
final angelCodeDataType = BMFontFileTypeHelper.guessType(data);
final fontInfo = switch angelCodeDataType
{
case TEXT(text):
FlxBMFontParser.fromText(text);
case XML(xml):
FlxBMFontParser.fromXml(xml);
case BINARY(bytes):
FlxBMFontParser.fromBinary(bytes);
};

// how much to move the cursor when going to the next line.
font.lineHeight = Std.parseInt(fast.node.common.att.lineHeight);
font.size = Std.parseInt(fast.node.info.att.size);
font.fontName = Std.string(fast.node.info.att.face);
font.bold = (Std.parseInt(fast.node.info.att.bold) != 0);
font.italic = (Std.parseInt(fast.node.info.att.italic) != 0);
font.lineHeight = fontInfo.common.lineHeight;
font.size = fontInfo.info.fontSize;
font.fontName = fontInfo.info.fontName;
font.bold = fontInfo.info.bold;
font.italic = fontInfo.info.italic;

var frame:FlxRect;
var frameHeight:Int;
var offset:FlxPoint;
var charStr:String;
var charCode:Int;
var xOffset:Int, yOffset:Int, xAdvance:Int;
Geokureli marked this conversation as resolved.
Show resolved Hide resolved

var chars = fast.node.chars;

for (char in chars.nodes.char)
for (char in fontInfo.chars)
{
frame = FlxRect.get();
frame.x = Std.parseInt(char.att.x); // X position within the bitmap image file.
frame.y = Std.parseInt(char.att.y); // Y position within the bitmap image file.
frame.width = Std.parseInt(char.att.width); // Width of the character in the image file.
frameHeight = Std.parseInt(char.att.height);
frame.x = char.x; // X position within the bitmap image file.
frame.y = char.y; // Y position within the bitmap image file.
frame.width = char.width; // Width of the character in the image file.
frameHeight = char.height;
frame.height = frameHeight; // Height of the character in the image file.
Geokureli marked this conversation as resolved.
Show resolved Hide resolved

// Number of pixels to move right before drawing this character.
xOffset = char.has.xoffset ? Std.parseInt(char.att.xoffset) : 0;
xOffset = char.xoffset;
// Number of pixels to move down before drawing this character.
yOffset = char.has.yoffset ? Std.parseInt(char.att.yoffset) : 0;
yOffset = char.yoffset;
// Number of pixels to jump right after drawing this character.
xAdvance = char.has.xadvance ? Std.parseInt(char.att.xadvance) : 0;
xAdvance = char.xadvance;

offset = FlxPoint.get(xOffset, yOffset);

font.minOffsetX = (font.minOffsetX < -xOffset) ? -xOffset : font.minOffsetX;

charCode = -1;
charStr = null;

if (char.has.letter) // The ASCII value of the character this line is describing. Helpful for debugging
if (char.id == null)
{
charStr = char.att.letter;
}
else if (char.has.id) // The character number in the ASCII table.
{
charCode = Std.parseInt(char.att.id);
}

if (charCode == -1 && charStr == null)
{
throw 'Invalid font xml data!';
}

if (charStr != null)
{
charStr = switch (charStr)
{
case "space": ' ';
case "&quot;": '"';
case "&amp;": '&';
case "&gt;": '>';
case "&lt;": '<';
default: charStr;
}

charCode = charStr.uCharCodeAt(0);
throw 'Invalid font data!';
}

font.addCharFrame(charCode, frame, offset, xAdvance);
font.addCharFrame(char.id, frame, offset, xAdvance);

if (charCode == SPACE_CODE)
if (char.id == SPACE_CODE)
{
font.spaceWidth = xAdvance;
}
Expand Down
110 changes: 110 additions & 0 deletions flixel/graphics/frames/bmfontutils/BMFont.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package flixel.graphics.frames.bmfontutils;

typedef BMFontInfoBlock =
{
public var fontSize:Null<Int>;
public var smooth:Bool;
public var unicode:Bool;
public var italic:Bool;
public var bold:Bool;
public var fixedHeight:Bool;
public var charSet:String;
public var stretchH:Null<Int>;
public var aa:Null<Int>;
public var paddingUp:Null<Int>;
public var paddingRight:Null<Int>;
public var paddingDown:Null<Int>;
public var paddingLeft:Null<Int>;
public var spacingHoriz:Null<Int>;
public var spacingVert:Null<Int>;
public var outline:Null<Int>;
public var fontName:String;
Geokureli marked this conversation as resolved.
Show resolved Hide resolved
};

typedef BMFontCommonBlock =
{
public var lineHeight:Null<Int>;
public var base:Null<Int>;
public var scaleW:Null<Int>;
public var scaleH:Null<Int>;
public var pages:Null<Int>;
public var isPacked:Bool;
public var alphaChnl:Null<Int>;
public var redChnl:Null<Int>;
public var greenChnl:Null<Int>;
public var blueChnl:Null<Int>;
};

typedef BMFontPageInfoBlock =
{
public var id:Null<Int>;
public var file:String;
};

typedef BMFontCharBlock =
{
public var id:Null<Int>;
public var x:Null<Int>;
public var y:Null<Int>;
public var width:Null<Int>;
public var height:Null<Int>;
public var xoffset:Null<Int>;
public var yoffset:Null<Int>;
public var xadvance:Null<Int>;
public var page:Null<Int>;
public var chnl:Null<Int>;
};

typedef BMFontKerningPair =
{
public var first:Null<Int>;
public var second:Null<Int>;
public var amount:Null<Int>;
};

class BMFont
{
public var info:BMFontInfoBlock;
public var common:BMFontCommonBlock;
public var pages:Array<BMFontPageInfoBlock>;
public var chars:Array<BMFontCharBlock>;
public var kerningPairs:Array<BMFontKerningPair>;

function new()
{
info = {
fontSize: null,
smooth: false,
unicode: false,
italic: false,
bold: false,
fixedHeight: false,
charSet: null,
stretchH: null,
aa: null,
paddingUp: null,
paddingRight: null,
paddingDown: null,
paddingLeft: null,
spacingHoriz: null,
spacingVert: null,
outline: null,
fontName: '',
};
common = {
lineHeight: null,
base: null,
scaleW: null,
scaleH: null,
pages: null,
isPacked: false,
alphaChnl: null,
redChnl: null,
greenChnl: null,
blueChnl: null,
};
pages = [];
chars = [];
kerningPairs = [];
}
}
102 changes: 102 additions & 0 deletions flixel/graphics/frames/bmfontutils/BMFontFileTypeHelper.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package flixel.graphics.frames.bmfontutils;

import flixel.system.FlxAssets.FlxAngelCodeAsset;
import haxe.io.Bytes;
import openfl.Assets;

enum BMFontFileType
{
TEXT(text:String);
XML(xml:Xml);
BINARY(bytes:Bytes);
}

class BMFontFileTypeHelper
{
/**
* A helper function that helps determine the type of BMFont descriptor from either the path or content of the file
* @param data The file path or the file content
* @return BMFontFileType
*/
public static function guessType(data:FlxAngelCodeAsset):BMFontFileType
{
if (data is Xml)
{
return XML(cast(data, Xml).firstElement());
}
else if (data is String)
{
var dataStr: String = cast data;
if (Assets.exists(dataStr))
{
// dataStr is a file path
var bytes = Assets.getBytes(dataStr);
if(bytes == null)
{
return detectFromText(Assets.getText(dataStr));
}
var fileType = detectFromBytes(bytes);
if (fileType == null)
{
return detectFromText(bytes.toString());
}
return detectFromBytes(bytes);
}
else
{
// dataStr is the content of a file as a string (can be xml or just plain text)
return detectFromText(dataStr);
}
}
else if (data is Bytes)
{
var bytes:Bytes = cast data;
var fileType = detectFromBytes(bytes);
if (fileType == null)
{
return detectFromText(bytes.toString());
}
}
return null;
}
static function safeParseXML(str:String)
{
// for js, Xml.parse throws if str is not valid XML but on desktop it just returns null
// This function will always return null if xml is invalid
try
{
var xml = Xml.parse(str);
return xml;
}
catch (e:Dynamic)
{
return null;
}
}

static function detectFromText(text:String):BMFontFileType
{
var xml = safeParseXML(text);
if (xml != null && xml.firstElement() != null)
{
return XML(xml.firstElement());
}
return TEXT(text);
}
static function detectFromBytes(bytes:Bytes):Null<BMFontFileType>
{
var expected = [66, 77, 70]; // 'B', 'M', 'F'
var isBinary = true;
for (i in 0...expected.length)
{
if (bytes.get(i) != expected[i])
{
isBinary = false;
break;
}
}
if (isBinary)
return BINARY(bytes);
return null;
}
}
Loading
Loading