hostile-takeover/SpiffLib/tbitmap.cs
2014-07-06 17:47:28 -07:00

839 lines
25 KiB
C#

using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace SpiffLib {
class TBitmap {
enum Align { None, Even, Odd, Both };
enum ColorType { Unknown, Shadow, Side, Transparent, Data };
enum Op {
EvenData1, EvenData2, EvenData3, EvenData4, EvenData5, EvenData6, EvenData7, EvenData8, EvenData9,
EvenData10, EvenData11, EvenData12, EvenData13, EvenData14, EvenData15, EvenData16, EvenData17,
EvenData18, EvenData19, EvenData20, EvenData21, EvenData22, EvenData23, EvenData24, EvenData25,
EvenData26, EvenData27, EvenData28, EvenData29, EvenData30, EvenData31, EvenData32, EvenData33,
EvenData34, EvenData35, EvenData36, EvenData37, EvenData38, EvenData39, EvenData40, EvenData41,
EvenData42, EvenData43, EvenData44, EvenData45, EvenData46, EvenData47, EvenData48,
OddData1, OddData2, OddData3, OddData4, OddData5, OddData6, OddData7, OddData8, OddData9,
OddData10, OddData11, OddData12, OddData13, OddData14, OddData15, OddData16, OddData17,
OddData18, OddData19, OddData20, OddData21, OddData22, OddData23, OddData24, OddData25,
OddData26, OddData27, OddData28, OddData29, OddData30, OddData31, OddData32, OddData33,
OddData34, OddData35, OddData36, OddData37, OddData38, OddData39, OddData40, OddData41,
OddData42, OddData43, OddData44, OddData45, OddData46, OddData47, OddData48,
EvenSide1, EvenSide2, EvenSide3, EvenSide4, EvenSide5, EvenSide6, EvenSide7, EvenSide8, EvenSide9,
EvenSide10, EvenSide11, EvenSide12, EvenSide13, EvenSide14, EvenSide15, EvenSide16,
OddSide1, OddSide2, OddSide3, OddSide4, OddSide5, OddSide6, OddSide7, OddSide8, OddSide9,
OddSide10, OddSide11, OddSide12, OddSide13, OddSide14, OddSide15, OddSide16,
Shadow1, Shadow2, Shadow3, Shadow4, Shadow5, Shadow6, Shadow7, Shadow8, Shadow9,
Shadow10, Shadow11, Shadow12, Shadow13, Shadow14, Shadow15, Shadow16, Shadow17,
Shadow18, Shadow19, Shadow20, Shadow21, Shadow22, Shadow23, Shadow24,
Transparent1, Transparent2, Transparent3, Transparent4, Transparent5, Transparent6, Transparent7, Transparent8, Transparent9,
Transparent10, Transparent11, Transparent12, Transparent13, Transparent14, Transparent15, Transparent16, Transparent17,
Transparent18, Transparent19, Transparent20, Transparent21, Transparent22, Transparent23, Transparent24, Transparent25,
Transparent26, Transparent27, Transparent28, Transparent29, Transparent30, Transparent31, Transparent32,
NextScan0, NextScan1, NextScan2, NextScan3, NextScan4, NextScan5, NextScan6, NextScan7, NextScan8, NextScan9,
NextScan10, NextScan11, NextScan12, NextScan13, NextScan14, NextScan15, NextScan16, NextScan17, NextScan18,
NextScan19, NextScan20, NextScan21, NextScan22, NextScan23, NextScan24, NextScan25, NextScan26, NextScan27,
NextScan28, NextScan29, NextScan30, NextScan31, NextScan32, NextScan33, NextScan34, NextScan35, NextScan36,
NextScan37, NextScan38, NextScan39, NextScan40, NextScan41, NextScan42, NextScan43, NextScan44, NextScan45,
NextScan46, NextScan47, NextScan48,
Align,
End
};
int m_cx;
int m_cy;
int m_yBaseline;
ScanData[] m_asdEven;
Palette m_pal;
ArrayList m_alsFrames = new ArrayList();
static Color s_clrShadow = Color.FromArgb(156, 212, 248);
static Color s_clrTransparent = Color.FromArgb(255, 0, 255);
static Color s_clrSideIndex0 = Color.FromArgb(0, 116, 232);
static Color s_clrSideIndex1 = Color.FromArgb(0, 96, 196);
static Color s_clrSideIndex2 = Color.FromArgb(0, 64, 120);
static Color s_clrSideIndex3 = Color.FromArgb(0, 48, 92);
static Color s_clrSideIndex4 = Color.FromArgb(0, 32, 64);
static int s_iclrShadow = -1;
static int s_iclrTransparent = -2;
static int s_iclrNotAssigned = -3;
static int s_iclrSideFirst = -8;
static int s_iclrSideLast = -4;
static int s_cpDataMax = 48;
static int s_cpSideMax = 16;
static int s_cpShadowMax = 24;
static int s_cpTransparentMax = 32;
static int s_cpNextScanMax = 48;
public TBitmap(string strFile, Palette pal) {
m_pal = pal;
Init(new Bitmap(strFile));
}
public TBitmap(Bitmap bm, Palette pal) {
m_pal = pal;
Init(bm);
}
public TBitmap(Palette pal) {
m_pal = pal;
}
void Init(Bitmap bm) {
m_cx = bm.Width;
m_cy = bm.Height;
m_yBaseline = Misc.FindBaseline(bm);
int[][] aaiclrScans = GetScans(bm);
m_asdEven = CompileScanData(Align.Even, aaiclrScans);
}
ScanData[] CompileScanData(Align alignMaster, int[][] aaiclrScans) {
ArrayList alsSd = new ArrayList();
Align alignStart = Align.Even;
for (int y = 0; y < aaiclrScans.GetLength(0); y++) {
if (alsSd.Count != 0)
alignStart = ((ScanData)alsSd[alsSd.Count - 1]).GetNextDataAlignment(alignStart);
alsSd.Add(new ScanData(alignMaster, alignStart, aaiclrScans[y]));
}
return (ScanData[])alsSd.ToArray(typeof(ScanData));
}
unsafe int[][] GetScans(Bitmap bm) {
// Special colors
int[][] aaiclrScans = new int[m_cy][];
// Lock down bits for speed
Rectangle rc = new Rectangle(0, 0, m_cx, m_cy);
BitmapData bmd = bm.LockBits(rc, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte *pbBase = (byte *)bmd.Scan0.ToPointer();
for (int y = 0; y < m_cy; y++) {
int[] aiclrScan = new int[m_cx];
for (int x = 0; x < m_cx; x++) {
// Get color
byte *pb = pbBase + y * bmd.Stride + x * 3;
Color clr = Color.FromArgb(pb[2], pb[1], pb[0]);
if (clr == s_clrTransparent) {
aiclrScan[x] = s_iclrTransparent;
continue;
}
if (clr == s_clrShadow) {
aiclrScan[x] = s_iclrShadow;
continue;
}
if (clr == s_clrSideIndex0) {
aiclrScan[x] = s_iclrSideFirst + 0;
continue;
}
if (clr == s_clrSideIndex1) {
aiclrScan[x] = s_iclrSideFirst + 1;
continue;
}
if (clr == s_clrSideIndex2) {
aiclrScan[x] = s_iclrSideFirst + 2;
continue;
}
if (clr == s_clrSideIndex3) {
aiclrScan[x] = s_iclrSideFirst + 3;
continue;
}
if (clr == s_clrSideIndex4) {
aiclrScan[x] = s_iclrSideFirst + 4;
continue;
}
aiclrScan[x] = m_pal.FindClosestEntry(clr);
}
aaiclrScans[y] = aiclrScan;
}
bm.UnlockBits(bmd);
return aaiclrScans;
}
class ScanData {
ArrayList m_alsIclr = new ArrayList();
ArrayList m_alsSideCodes = new ArrayList();
ArrayList m_alsOps = new ArrayList();
struct Run {
public int cp;
public ColorType ct;
public Align align;
public int[] aiclr;
}
public ScanData(Align alignMaster, Align alignData, int[] aiclrScan) {
CompileRuns(alignData, GetRuns(alignMaster, aiclrScan));
}
public Align GetNextDataAlignment(Align alignStart) {
if (alignStart == Align.Even) {
return (m_alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even;
} else {
return (m_alsIclr.Count & 1) == 0 ? Align.Odd : Align.Even;
}
}
public int[] GetColorData() {
return (int[])m_alsIclr.ToArray(typeof(int));
}
public int[] GetSideCodes() {
return (int[])m_alsSideCodes.ToArray(typeof(int));
}
public Op[] GetOps() {
return (Op[])m_alsOps.ToArray(typeof(Op));
}
void AddSideCodes(Align align, int[] aiclr) {
m_alsSideCodes.AddRange(EncodeSideColors(align, aiclr));
}
ColorType GetColorType(int iclr) {
if (iclr == s_iclrTransparent)
return ColorType.Transparent;
if (iclr == s_iclrShadow)
return ColorType.Shadow;
if (iclr >= s_iclrSideFirst && iclr <= s_iclrSideLast)
return ColorType.Side;
return ColorType.Data;
}
Run[] GetRuns(Align alignScan, int[] aiclrScan) {
// First calc and remember the runs
ArrayList alsCtRuns = new ArrayList();
ArrayList alsCounts = new ArrayList();
ColorType ctRun = ColorType.Unknown;
int iiclrFirst = 0;
for (int iiclr = 0; iiclr < aiclrScan.Length; iiclr++) {
// Classify color
ColorType ctCurrent = GetColorType(aiclrScan[iiclr]);
// Continue if we're in a run
if (ctRun == ctCurrent)
continue;
// Add the run type and the count of pixels this is
if (ctRun != ColorType.Unknown) {
alsCtRuns.Add(ctRun);
alsCounts.Add(iiclr - iiclrFirst);
}
// Start a run with this color type
iiclrFirst = iiclr;
ctRun = ctCurrent;
}
// Add the last run
alsCtRuns.Add(ctRun);
alsCounts.Add(aiclrScan.Length - iiclrFirst);
// Now all the scan is classified in terms of runs of color types. Now create runs.
ArrayList alsRun = new ArrayList();
int iiclrRun = 0;
for (int i = 0; i < alsCtRuns.Count; i++) {
AddRun(alsRun, alignScan, iiclrRun, aiclrScan, (int)alsCounts[i], (ColorType)alsCtRuns[i]);
iiclrRun += (int)alsCounts[i];
}
// All done
return (Run[])alsRun.ToArray(typeof(Run));
}
void AddRun(ArrayList alsRun, Align alignScan, int iiclrRun, int[] aiclrScan, int cpRun, ColorType ctRun) {
// Figure out the alignment of this run at the dst if drawn
Align alignDst;
if (alignScan == Align.Even) {
alignDst = (iiclrRun & 1) != 0 ? Align.Odd : Align.Even;
} else {
alignDst = (iiclrRun & 1) == 0 ? Align.Odd : Align.Even;
}
// Copy run data, figure required alignment
int[] aiclrRun = null;
Align alignRun = Align.None;
switch (ctRun) {
case ColorType.Data:
case ColorType.Side:
alignRun = alignDst;
aiclrRun = new int[cpRun];
for (int iiclr = iiclrRun; iiclr < iiclrRun + cpRun; iiclr++)
aiclrRun[iiclr - iiclrRun] = aiclrScan[iiclr];
break;
case ColorType.Shadow:
break;
case ColorType.Transparent:
break;
default:
Debug.Assert(false);
break;
}
// Add this run
Run run;
run.ct = ctRun;
run.cp = cpRun;
run.align = alignRun;
run.aiclr = aiclrRun;
alsRun.Add(run);
}
int[] EncodeSideColors(Align align, int[] aiclr) {
int[] asc = new int[(aiclr.Length + 1) / 2];
for (int isc = 0; isc < asc.Length; isc++) {
int iclr1 = aiclr[isc * 2];
int iclr2 = s_iclrSideFirst;
if (isc * 2 + 1 < aiclr.Length)
iclr2 = aiclr[isc * 2 + 1];
asc[isc] = (iclr1 - s_iclrSideFirst) * 2 * 16 + (iclr2 - s_iclrSideFirst) * 2;
}
return asc;
}
Run[] ChopRuns(Run[] arun) {
// Chop the runs into compatible pieces. Most runs won't get chopped, only runs whose
// length exceeds the op handlers' abilities. Chop wisely, especially runs with
// alignment and / or optimal size issues.
ArrayList alsRuns = new ArrayList();
foreach (Run run in arun) {
int cp = run.cp;
Align align = run.align;
Align alignNext = align;
int[] aiclr;
switch (run.ct) {
case ColorType.Data:
aiclr = (int[])run.aiclr.Clone();
while (aiclr.Length != 0) {
// Figure out the run length to use if this run is being chopped.
// Best bet is to ensure dword moves across the chop
int cpT = aiclr.Length;
if (cpT > s_cpDataMax) {
if (align == Align.Even) {
cpT = s_cpDataMax;
} else {
cpT = ((s_cpDataMax - 1) & ~3) + 1;
alignNext = Align.Even;
}
}
// Add the run
Run runT = run;
runT.cp = cpT;
runT.align = align;
runT.aiclr = new int[cpT];
for (int iiclr = 0; iiclr < cpT; iiclr++)
runT.aiclr[iiclr] = aiclr[iiclr];
alsRuns.Add(runT);
// Adjust the colors left
int ciclrLeft = aiclr.Length - cpT;
int[] aiclrT = new int[ciclrLeft];
for (int iiclr = 0; iiclr < ciclrLeft; iiclr++)
aiclrT[iiclr] = aiclr[iiclr + cpT];
aiclr = aiclrT;
align = alignNext;
}
break;
case ColorType.Side:
aiclr = (int[])run.aiclr.Clone();
while (aiclr.Length != 0) {
// Figure out the run length to use if this run is being chopped.
// Best bet is to ensure dword moves across the chop
int cpT = aiclr.Length;
if (cpT > s_cpSideMax) {
if (align == Align.Even) {
cpT = s_cpSideMax;
} else {
cpT = ((s_cpSideMax - 1) & ~3) + 1;
alignNext = Align.Even;
}
}
// Add the run
Run runT = run;
runT.cp = cpT;
runT.align = align;
runT.aiclr = new int[cpT];
for (int iiclr = 0; iiclr < cpT; iiclr++)
runT.aiclr[iiclr] = aiclr[iiclr];
alsRuns.Add(runT);
// Adjust the colors left
int ciclrLeft = aiclr.Length - cpT;
int[] aiclrT = new int[ciclrLeft];
for (int iiclr = 0; iiclr < ciclrLeft; iiclr++)
aiclrT[iiclr] = aiclr[iiclr + cpT];
aiclr = aiclrT;
align = alignNext;
}
break;
case ColorType.Shadow:
while (cp != 0) {
int cpT = cp > s_cpShadowMax ? s_cpShadowMax : cp;
Run runT = run;
runT.cp = cpT;
alsRuns.Add(runT);
cp -= cpT;
}
break;
case ColorType.Transparent:
while (cp != 0) {
int cpT = cp > s_cpTransparentMax ? s_cpTransparentMax : cp;
Run runT = run;
runT.cp = cpT;
alsRuns.Add(runT);
cp -= cpT;
}
break;
}
}
return (Run[])alsRuns.ToArray(typeof(Run));
}
void CompileRuns(Align alignStart, Run[] arun) {
// First chop the run into compatible pieces.
arun = ChopRuns(arun);
// Now go through all runs in the scan and encode into ops and data
foreach (Run run in arun) {
switch (run.ct) {
case ColorType.Data:
Align align = GetNextDataAlignment(alignStart);
if (run.align != align && run.cp != 1) {
m_alsOps.Add(Op.Align);
m_alsIclr.Add(s_iclrNotAssigned);
}
if (run.align == Align.Even) {
m_alsOps.Add((Op)(run.cp + (int)Op.EvenData1 - 1));
} else {
m_alsOps.Add((Op)(run.cp + (int)Op.OddData1 - 1));
}
m_alsIclr.AddRange(run.aiclr);
break;
case ColorType.Side:
if (run.align == Align.Even) {
m_alsOps.Add((Op)(run.cp + (int)Op.EvenSide1 - 1));
} else {
m_alsOps.Add((Op)(run.cp + (int)Op.OddSide1 - 1));
}
AddSideCodes(run.align, run.aiclr);
break;
case ColorType.Shadow:
m_alsOps.Add((Op)(run.cp + (int)Op.Shadow1 - 1));
break;
case ColorType.Transparent:
m_alsOps.Add((Op)(run.cp + (int)Op.Transparent1 - 1));
break;
default:
Debug.Assert(false);
break;
}
}
}
}
ArrayList SerializeOps(ScanData[] asd) {
// Serialize ops. Remove trailing and beginning transparency between scans
// and replace with NextScan ops.
ArrayList alsOps = new ArrayList();
ArrayList alsT = new ArrayList();
int cpSkip = -1;
foreach (ScanData sd in asd) {
alsT.Clear();
alsT.AddRange(sd.GetOps());
// If cpSkip != -1 then we've removed some trailing transparency
// from the last scan. Search the start of this new scan.
if (cpSkip != -1) {
int iop = 0;
for (; iop < alsT.Count; iop++) {
// Add up the transparency
Op op = (Op)alsT[iop];
if (op >= Op.Transparent1 && op <= Op.Transparent32) {
cpSkip += op - Op.Transparent1 + 1;
continue;
}
break;
}
// Hit a non-transparent op or end of list. Remove found transparent ops
alsT.RemoveRange(0, iop);
// Max is...
int cpMax = m_cx > s_cpNextScanMax ? s_cpNextScanMax : m_cx;
// If there is too much transparency to endcode in one
// NextScan op, cut into pieces.
int cpExtra = cpSkip - cpMax;
if (cpExtra > 0) {
cpSkip -= cpExtra;
while (cpExtra != 0) {
int cpT = cpExtra <= s_cpTransparentMax ? cpExtra : s_cpTransparentMax;
alsT.Insert(0, Op.Transparent1 + cpT - 1);
cpExtra -= cpT;
}
}
// Insert NextScan op
alsT.Insert(0, Op.NextScan0 + cpSkip);
}
// Now remove trailing transparency if there is any.
cpSkip = 0;
int iopTrailing = -1;
for (int iop = 0; iop < alsT.Count; iop++) {
Op op = (Op)alsT[iop];
if (op >= Op.Transparent1 && op <= Op.Transparent32) {
if (iopTrailing == -1)
iopTrailing = iop;
cpSkip += op - Op.Transparent1 + 1;
continue;
} else {
iopTrailing = -1;
cpSkip = 0;
}
}
// Remove the trailing transparency
if (iopTrailing != -1) {
// Remove this transparency
alsT.RemoveRange(iopTrailing, alsT.Count - iopTrailing);
// If we've skipped more than the largest EndScan, insert some
// transparency.
int cpExtra = cpSkip - s_cpNextScanMax;
if (cpExtra > 0) {
cpSkip -= cpExtra;
while (cpExtra != 0) {
int cpT = cpExtra <= s_cpTransparentMax ? cpExtra : s_cpTransparentMax;
alsT.Add(Op.Transparent1 + cpT - 1);
cpExtra -= cpT;
}
}
}
// alsT is ready to add to the ops list
alsOps.AddRange(alsT);
}
// Add End op
alsOps.Add(Op.End);
return alsOps;
}
byte[] SerializeScanData()
{
// Combine all ScanData data
ScanData[] asd = m_asdEven;
ArrayList alsSideCodes = new ArrayList();
ArrayList alsIclr = new ArrayList();
foreach (ScanData sd in asd) {
alsSideCodes.AddRange(sd.GetSideCodes());
alsIclr.AddRange(sd.GetColorData());
}
// Make ops. Add side codes to op stream to unify stream.
ArrayList alsOpsT = SerializeOps(asd);
ArrayList alsOps = new ArrayList();
foreach (Op op in alsOpsT) {
alsOps.Add(op);
if (op >= Op.EvenSide1 && op <= Op.EvenSide16) {
int csc = (((op - Op.EvenSide1) + 1) + 1) / 2;
for (int isc = 0; isc < csc; isc++)
alsOps.Add(alsSideCodes[isc]);
alsSideCodes.RemoveRange(0, csc);
}
if (op >= Op.OddSide1 && op <= Op.OddSide16) {
int csc = (((op - Op.OddSide1) + 1) + 1) / 2;
for (int isc = 0; isc < csc; isc++)
alsOps.Add(alsSideCodes[isc]);
alsSideCodes.RemoveRange(0, csc);
}
}
// Remember count of bytes needed for aligned data. Add one for both alignements.
// This is needed during compiling. Make it an even count.
int cbaiclrUnpacked = ((alsIclr.Count + 1) + 1) & ~1;
// Create the packed, unaligned color data
ArrayList alsIclrPacked = new ArrayList();
foreach (int iclr in alsIclr) {
if (iclr != s_iclrNotAssigned)
alsIclrPacked.Add(iclr);
}
// Serialize:
// public ushort ibaiclr;
// public ushort cbaiclrUnpacked;
// public byte[] aop;
// public byte[] aiclr;
// Write placeholders
BinaryWriter bwtr = new BinaryWriter(new MemoryStream());
bwtr.Write((ushort)0);
bwtr.Write((ushort)0);
// Data
foreach (Op op in alsOps)
bwtr.Write((byte)op);
int ibaiclr = (ushort)bwtr.BaseStream.Position;
foreach (int iclr in alsIclrPacked)
bwtr.Write((byte)iclr);
// Fix up pointers
int cb = (int)bwtr.BaseStream.Length;
bwtr.BaseStream.Seek(0, SeekOrigin.Begin);
bwtr.Write((ushort)Misc.SwapUShort((ushort)ibaiclr));
bwtr.Write((ushort)Misc.SwapUShort((ushort)cbaiclrUnpacked));
// Return buffer
byte[] ab = new byte[cb];
bwtr.BaseStream.Seek(0, SeekOrigin.Begin);
bwtr.BaseStream.Read(ab, 0, cb);
bwtr.Close();
return ab;
}
static byte[] Serialize(TBitmap[] atbm) {
//struct TBitmapHeader:
// ctbm
// TBitmapEntry[] atbme;
// word cx;
// word cy;
// word yBaseline;
// word ibsd;
// word cbsd;
// ScanData[] asd;
// Write header info
BinaryWriter bwtr = new BinaryWriter(new MemoryStream());
bwtr.Write(Misc.SwapUShort((ushort)atbm.Length));
// Serialize TBitmapEntry's
ArrayList alsSdBytes = new ArrayList();
int ibCurrent = 2 + 10 * atbm.Length;
for (int itbm = 0; itbm < atbm.Length; itbm++) {
bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_cx));
bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_cy));
bwtr.Write(Misc.SwapUShort((ushort)atbm[itbm].m_yBaseline));
bwtr.Write(Misc.SwapUShort((ushort)ibCurrent));
byte[] ab = atbm[itbm].SerializeScanData();
alsSdBytes.AddRange(ab);
bwtr.Write(Misc.SwapUShort((ushort)ab.Length));
ibCurrent += ab.Length;
Debug.Assert(ibCurrent < ushort.MaxValue);
}
// Write sd bytes
bwtr.Write((byte[])alsSdBytes.ToArray(typeof(byte)));
// Done
byte[] abT = new Byte[bwtr.BaseStream.Length];
bwtr.BaseStream.Seek(0, SeekOrigin.Begin);
bwtr.BaseStream.Read(abT, 0, abT.Length);
bwtr.Close();
return abT;
}
static public void Save(Bitmap[] abm, Palette pal, string strFile) {
// Open file for writing
BinaryWriter bwtr = new BinaryWriter(new FileStream(strFile, FileMode.Create, FileAccess.Write));
// Convert into tbm's
TBitmap[] atbm = new TBitmap[abm.Length];
for (int ibm = 0; ibm < abm.Length; ibm++)
atbm[ibm] = new TBitmap(abm[ibm], pal);
// Serialize the whole thing
bwtr.Write(Serialize(atbm));
// All done
bwtr.Close();
}
static public void SaveFont(string strFileBitmap, Palette pal, string strFileAscii, string strFileSave) {
// Get the character order
TextReader tr = new StreamReader(strFileAscii);
string strAscii = tr.ReadLine();
tr.Close();
// Get the character count
int cch = strAscii.Length;
// Load the image, lose scaling factor
Bitmap bmFile = new Bitmap(strFileBitmap);
Bitmap bm = Misc.NormalizeBitmap(bmFile);
bmFile.Dispose();
// Turn this on to see the character -> glyph mapping as it happens (help for
// finding 'font bugs'). Set a breakpoint below on frm.Dispose().
#if SHOWFONT
Form frm = new Form();
frm.Height = 1000;
frm.Show();
Graphics gT = frm.CreateGraphics();
gT.InterpolationMode = InterpolationMode.NearestNeighbor;
int yDst = 0;
int xDst = 0;
#endif
// Scan the bitmap for widths
int xLast = 0;
int ich = 0;
byte[] acxChar = new byte[256];
for (int x = 0; x < bm.Width; x++) {
if (bm.GetPixel(x, 0) != Color.FromArgb(255, 0, 255)) {
Debug.Assert(ich < cch);
acxChar[strAscii[ich]] = (byte)(x - xLast);
#if SHOWFONT
gT.DrawString(strAscii[ich].ToString(), frm.Font, new SolidBrush(frm.ForeColor), new PointF(xDst, yDst));
Rectangle rcDst = new Rectangle(xDst + 20, yDst + 2, (x - xLast), bm.Height);
Rectangle rcSrc = new Rectangle(xLast, 1, x - xLast, bm.Height);
gT.DrawImage(bm, rcDst, rcSrc, GraphicsUnit.Pixel);
yDst += Math.Max(bm.Height, frm.Font.Height);
if (yDst > frm.ClientRectangle.Height) {
xDst += 50;
yDst = 0;
}
gT.Flush();
Application.DoEvents();
#endif
ich++;
xLast = x;
}
}
#if SHOWFONT
gT.Dispose();
frm.Dispose();
#endif
if (ich != cch) {
MessageBox.Show(String.Format("Expecting {0} characters but found {2}{1}.",
cch, ich, ich < cch ? "only " : ""), "bcr2 - Font Compilation Error");
Debug.Assert(ich == cch - 1);
}
int cy = bm.Height - 1;
// Save serialization
ArrayList alsSdEven = new ArrayList();
int xT = 0;
int ichDefault = -1;
for (ich = 0; ich < cch; ich++) {
// ? is the default "no glyph" character
if (strAscii[ich] == '?')
ichDefault = ich;
// Get subimage
int cx = acxChar[strAscii[ich]];
Rectangle rcT = new Rectangle(xT, 1, cx, cy);
xT += cx;
Bitmap bmT = new Bitmap(cx, cy, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bmT);
g.DrawImage(bm, 0, 0, rcT, GraphicsUnit.Pixel);
g.Dispose();
// Compile scan data
TBitmap tbm = new TBitmap(bmT, pal);
bmT.Dispose();
// Save scan data serialization
alsSdEven.Add(tbm.SerializeScanData());
}
//FontHeader {
// word cy;
// byte acxChar[256];
// word mpchibsdEven[256];
// ScanData asd[1];
//};
// First serialize scan data
ArrayList alsIbsdEven = new ArrayList();
ArrayList alsSd = new ArrayList();
foreach (byte[] absd in alsSdEven) {
if ((alsSd.Count & 1) != 0)
alsSd.Add((byte)0);
alsIbsdEven.Add(alsSd.Count);
alsSd.AddRange(absd);
}
// Write out to file
BinaryWriter bwtr = new BinaryWriter(new FileStream(strFileSave, FileMode.Create, FileAccess.Write));
// Height
bwtr.Write(Misc.SwapUShort((ushort)cy));
// Ascii ordered char widths in bytes. First init 0's to width of '?'
if (ichDefault != -1) {
for (ich = 0; ich < acxChar.Length; ich++) {
if (acxChar[ich] == 0) {
acxChar[ich] = acxChar[strAscii[ichDefault]];
}
}
}
bwtr.Write(acxChar);
// Ascii ordered offsets to even scan data (even)
// Fill unused entries to entry for '?'
int[] aibsdEven = new int[256];
for (int ibsd = 0; ibsd < aibsdEven.Length; ibsd++)
aibsdEven[ibsd] = -1;
for (int i = 0; i < cch; i++)
aibsdEven[strAscii[i]] = (int)alsIbsdEven[i];
if (ichDefault != -1) {
for (int ibsd = 0; ibsd < aibsdEven.Length; ibsd++) {
if (aibsdEven[ibsd] == -1) {
aibsdEven[ibsd] = (int)alsIbsdEven[ichDefault];
}
}
}
// Write it out
int cbHeader = 2 + 256 + 512;
for (int i = 0; i < 256; i++)
bwtr.Write(Misc.SwapUShort((ushort)(cbHeader + aibsdEven[i])));
// Now save scan data
bwtr.Write((byte[])alsSd.ToArray(typeof(byte)));
// Done
bwtr.Close();
}
}
}