mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
839 lines
25 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|