Skip to content

Commit

Permalink
Add support for FZX format
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaRGB committed Feb 3, 2018
1 parent 077d531 commit 1ed746d
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.kreative.bitsnpicas.BitmapFont;
import com.kreative.bitsnpicas.BitmapFontExporter;
import com.kreative.bitsnpicas.exporter.BDFBitmapFontExporter;
import com.kreative.bitsnpicas.exporter.FZXBitmapFontExporter;
import com.kreative.bitsnpicas.exporter.NFNTBitmapFontExporter;
import com.kreative.bitsnpicas.exporter.RFontBitmapFontExporter;
import com.kreative.bitsnpicas.exporter.RawBitmapFontExporter;
Expand Down Expand Up @@ -217,6 +218,9 @@ public void actionPerformed(ActionEvent e) {
(pngColorBlue.getNumber().intValue() << 0)
);
break;
case FZX:
exporter = new FZXBitmapFontExporter();
break;
case VGA:
exporter = new RawBitmapFontExporter();
break;
Expand Down Expand Up @@ -260,6 +264,7 @@ private static enum Format {
DFONT("Mac OS Classic Font Suitcase (Data Fork)", ".dfont", "mac"),
SFONT("PNG (SDL SFont)", ".png", "color"),
RFONT("PNG (RFont)", ".png", "color"),
FZX("FZX (ZX Spectrum)", ".fzx", "none"),
VGA("VGA Character Set", ".ft", "none"),
SBF("SBF (Sabriel Font)", ".sbf", "none");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.kreative.bitsnpicas.exporter.KBnPVectorFontExporter;
import com.kreative.bitsnpicas.importer.BDFBitmapFontImporter;
import com.kreative.bitsnpicas.importer.DSFBitmapFontImporter;
import com.kreative.bitsnpicas.importer.FZXBitmapFontImporter;
import com.kreative.bitsnpicas.importer.KBnPBitmapFontImporter;
import com.kreative.bitsnpicas.importer.KBnPVectorFontImporter;
import com.kreative.bitsnpicas.importer.NFNTBitmapFontImporter;
Expand Down Expand Up @@ -108,6 +109,9 @@ public static JFrame openFonts(File file) {
JFrame f = new BinaryBitmapFontImporterFrame(file);
f.setVisible(true);
return f;
} else if (lname.endsWith(".fzx")) {
BitmapFont[] fonts = new FZXBitmapFontImporter().importFont(file);
return openFonts(file, null, fonts);
} else if (lname.endsWith(".dsf")) {
BitmapFont[] fonts = new DSFBitmapFontImporter().importFont(file);
return openFonts(file, null, fonts);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.kreative.bitsnpicas.exporter;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.kreative.bitsnpicas.BitmapFont;
import com.kreative.bitsnpicas.BitmapFontExporter;
import com.kreative.bitsnpicas.BitmapFontGlyph;

public class FZXBitmapFontExporter implements BitmapFontExporter {
@Override
public byte[] exportFontToBytes(BitmapFont font) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
exportFontToStream(font, os);
os.close();
return os.toByteArray();
}

@Override
public void exportFontToStream(BitmapFont font, OutputStream os) throws IOException {
int ascent = font.getLineAscent();
int height = ascent + font.getLineDescent();
int tracking = 255;
int lastchar = 255;
while (lastchar > 32 && !font.containsCharacter(zxcp(lastchar))) lastchar--;
for (int ch = 32; ch <= lastchar; ch++) {
BitmapFontGlyph g = font.getCharacter(zxcp(ch));
if (g != null) {
int rsb = g.getCharacterWidth() - (g.getGlyphOffset() + g.getGlyphWidth());
if (rsb <= 0) { tracking = 0; break; }
else if (rsb < tracking) tracking = rsb;
}
}
if (height < 1) height = 1;
if (height > 255) height = 255;

int end = (lastchar - 30) * 3;
int ptr = end + 2;
int[] offset = new int[lastchar - 31];
int[] kern = new int[lastchar - 31];
int[] shift = new int[lastchar - 31];
int[] width = new int[lastchar - 31];
byte[][] cdef = new byte[lastchar - 31][];
for (int i = 0, ch = 32; ch <= lastchar; ch++, i++) {
BitmapFontGlyph g = font.getCharacter(zxcp(ch));
if (g == null) {
offset[i] = ptr;
kern[i] = 0;
shift[i] = 0;
width[i] = 1;
cdef[i] = new byte[0];
} else {
int lsb = g.getGlyphOffset();
int tsb = ascent - g.getGlyphAscent();
int rsb = g.getCharacterWidth() - (lsb + g.getGlyphWidth());
offset[i] = ptr;
kern[i] = (lsb <= -3) ? 3 : (lsb >= 0) ? 0 : -lsb;
shift[i] = (tsb <= 0) ? 0 : (tsb >= 15) ? 15 : tsb;
int kernExtra = lsb + kern[i];
int shiftExtra = tsb - shift[i];
int widthExtra = rsb - tracking;
int zxw = g.getGlyphWidth() + kernExtra + widthExtra;
int zxh = g.getGlyphHeight() + shiftExtra;
int bpr = (zxw <= 8) ? 1 : 2;
width[i] = (zxw <= 1) ? 1 : (zxw >= 16) ? 16 : zxw;
cdef[i] = new byte[zxh * bpr];
boolean cdefChanged = false;
byte[][] gd = g.getGlyph();
for (int gdy = -shiftExtra, cdy = 0, zxy = 0; zxy < zxh; zxy++, cdy += bpr, gdy++) {
if (gdy >= 0 && gdy < gd.length) {
for (int gdx = -kernExtra, cdx = 0x8000, zxx = 0; zxx < width[i]; zxx++, cdx >>= 1, gdx++) {
if (gdx >= 0 && gdx < gd[gdy].length && gd[gdy][gdx] < 0) {
if (cdx < 0x100) cdef[i][cdy + 1] |= cdx;
else cdef[i][cdy] |= (cdx >> 8);
cdefChanged = true;
}
}
}
}
if (cdefChanged) {
ptr += cdef[i].length;
} else {
kern[i] = 0;
shift[i] = 0;
cdef[i] = new byte[0];
}
}
}

os.write(height);
os.write(tracking);
os.write(lastchar);
for (int o = 3, i = 0, ch = 32; ch <= lastchar; ch++, i++, o += 3) {
int ok = ((offset[i] - o) & 0x3FFF) | (kern[i] << 14);
int sw = (shift[i] << 4) | ((width[i] - 1) & 0x0F);
os.write(ok);
os.write(ok >> 8);
os.write(sw);
}
os.write(ptr - end);
os.write((ptr - end) >> 8);
for (byte[] cd : cdef) os.write(cd);
}

@Override
public void exportFontToFile(BitmapFont font, File file) throws IOException {
FileOutputStream os = new FileOutputStream(file);
exportFontToStream(font, os);
os.close();
}

private int zxcp(int ch) {
if (ch == 96) return 163;
if (ch == 127) return 169;
if (ch < 128) return ch;
return (0xF000 + ch);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.kreative.bitsnpicas.importer;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import com.kreative.bitsnpicas.BitmapFont;
import com.kreative.bitsnpicas.BitmapFontGlyph;
import com.kreative.bitsnpicas.BitmapFontImporter;
import com.kreative.bitsnpicas.Font;

public class FZXBitmapFontImporter implements BitmapFontImporter {
private static final char[] X_HEIGHT_CHARS = new char[]{'x', 'X', '0', '!'};

@Override
public BitmapFont[] importFont(byte[] data) throws IOException {
// HEADER
if (data.length < 3) return new BitmapFont[0];
int height = data[0] & 0xFF;
int tracking = data[1] & 0xFF;
int lastchar = data[2] & 0xFF;
if (lastchar < 32) return new BitmapFont[0];

// TABLE
int end = (lastchar - 30) * 3;
if (data.length < end + 2) return new BitmapFont[0];
int[] offset = new int[lastchar - 30];
int[] kern = new int[lastchar - 31];
int[] shift = new int[lastchar - 31];
int[] width = new int[lastchar - 31];
for (int o = 3, i = 0, ch = 32; ch <= lastchar; ch++, i++, o += 3) {
int ok = (data[o] & 0xFF) | ((data[o + 1] & 0xFF) << 8);
int sw = (data[o + 2] & 0xFF);
offset[i] = (ok & 0x3FFF) + o;
kern[i] = ok >> 14;
shift[i] = sw >> 4;
width[i] = (sw & 0x0F) + 1;
}
offset[lastchar - 31] = ((data[end] & 0xFF) | ((data[end + 1] & 0xFF) << 8)) + end;

// CHARACTER DEFINITIONS
byte[][][] gd = new byte[lastchar - 31][][];
for (int i = 0, ch = 32; ch <= lastchar; ch++, i++) {
int bpr = ((width[i] <= 8) ? 1 : 2);
int rows = (offset[i + 1] - offset[i]) / bpr;
if (rows < 0) rows = 0;
gd[i] = new byte[rows][width[i]];
for (int o = offset[i], y = 0; y < rows; y++, o += bpr) {
int row = (o < data.length) ? ((data[o] & 0xFF) << 8) : 0;
row |= (o + 1 < data.length) ? (data[o + 1] & 0xFF) : 0;
for (int m = 0x8000, x = 0; x < width[i]; x++, m >>= 1) {
if ((row & m) != 0) gd[i][y][x] = -1;
}
}
}

int ascent = height;
int descent = 0;
int xheight = 0;
for (char x : X_HEIGHT_CHARS) {
if (lastchar >= x) {
byte[][] gdx = gd[x - 32];
xheight = gdx.length;
ascent = shift[x - 32] + xheight;
descent = height - ascent;
break;
}
}

BitmapFont f = new BitmapFont(ascent, descent, ascent, descent, xheight, 0);
for (int i = 0, ch = 32; ch <= lastchar; ch++, i++) {
int cp = (
(ch == 96) ? 163 :
(ch == 127) ? 169 :
(ch < 128) ? ch :
(0xF000 + ch)
);
BitmapFontGlyph g = new BitmapFontGlyph(
gd[i], -kern[i],
width[i] - kern[i] + tracking,
ascent - shift[i]
);
f.putCharacter(cp, g);
}
return new BitmapFont[]{f};
}

@Override
public BitmapFont[] importFont(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1048576]; int read;
while ((read = in.read(buf)) >= 0) out.write(buf, 0, read);
out.close();
return importFont(out.toByteArray());
}

@Override
public BitmapFont[] importFont(File file) throws IOException {
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1048576]; int read;
while ((read = in.read(buf)) >= 0) out.write(buf, 0, read);
out.close();
in.close();
BitmapFont[] f = importFont(out.toByteArray());
if (f.length > 0) {
String name = file.getName();
if (name.toLowerCase().endsWith(".fzx")) {
name = name.substring(0, name.length() - 4);
}
for (BitmapFont ff : f) {
ff.setName(Font.NAME_FAMILY, name);
}
}
return f;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ private static ImportFontResult importFont(File file) throws IOException {
} else if (lname.endsWith(".png")) {
ret.fonts = new SRFontBitmapFontImporter().importFont(file);
ret.nameType = BitmapFont.NAME_FAMILY_AND_STYLE;
} else if (lname.endsWith(".fzx")) {
ret.fonts = new FZXBitmapFontImporter().importFont(file);
ret.nameType = BitmapFont.NAME_FAMILY;
} else if (lname.endsWith(".dsf")) {
ret.fonts = new DSFBitmapFontImporter().importFont(file);
ret.nameType = BitmapFont.NAME_FAMILY;
Expand Down Expand Up @@ -371,6 +374,10 @@ private static boolean exportFont(BitmapFont font, String name, Options o) throw
File out = getOutputFile(o.dest, name, ".png");
new RFontBitmapFontExporter().exportFontToFile(font, out);
return true;
} else if (format.equals("fzx")) {
File out = getOutputFile(o.dest, name, ".fzx");
new FZXBitmapFontExporter().exportFontToFile(font, out);
return true;
} else if (format.equals("vga") || format.equals("raw")) {
File out = getOutputFile(o.dest, name, ".ft");
new RawBitmapFontExporter().exportFontToFile(font, out);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public static void main(String[] args) {
open(new NFNTBitmapFontImporter(), file);
} else if (lname.endsWith(".png")) {
open(new SRFontBitmapFontImporter(), file);
} else if (lname.endsWith(".fzx")) {
open(new FZXBitmapFontImporter(), file);
} else if (lname.endsWith(".dsf")) {
open(new DSFBitmapFontImporter(), file);
} else if (lname.endsWith(".s10")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public static void main(String[] args) {
File mfile = sibling(file, 4, "Mask.png");
if (!mfile.exists()) ViewFont.open(imp, file);
else new ViewFont2(imp.importFont(file)[0], imp.importFont(mfile)[0]);
} else if (lname.endsWith(".fzx")) {
FZXBitmapFontImporter imp = new FZXBitmapFontImporter();
File mfile = sibling(file, 4, "Mask.fzx");
if (!mfile.exists()) ViewFont.open(imp, file);
else new ViewFont2(imp.importFont(file)[0], imp.importFont(mfile)[0]);
} else if (lname.endsWith(".dsf")) {
DSFBitmapFontImporter imp = new DSFBitmapFontImporter();
File mfile = sibling(file, 4, "Mask.dsf");
Expand Down

0 comments on commit 1ed746d

Please sign in to comment.