mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
1888 lines
50 KiB
C#
1888 lines
50 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Diagnostics;
|
|
|
|
namespace SpiffLib {
|
|
class TBitmapSR {
|
|
enum Op {
|
|
EvenData1, // 0
|
|
EvenData1_Inc, // 1
|
|
EvenData2, // 2
|
|
EvenData2_Inc, // 3
|
|
EvenData3, // 4
|
|
EvenData3_Inc, // 5
|
|
EvenData4, // 6
|
|
EvenData4_Inc, // 7
|
|
EvenData5, // 8
|
|
EvenData5_Inc, // 9
|
|
EvenData6, // 10
|
|
EvenData6_Inc, // 11
|
|
EvenData7, // 12
|
|
EvenData7_Inc, // 13
|
|
EvenData8, // 14
|
|
EvenData8_Inc, // 15
|
|
EvenData9, // 16
|
|
EvenData9_Inc, // 17
|
|
EvenData10, // 18
|
|
EvenData10_Inc, // 19
|
|
EvenData11, // 20
|
|
EvenData11_Inc, // 21
|
|
EvenData12, // 22
|
|
EvenData12_Inc, // 23
|
|
EvenData13, // 24
|
|
EvenData13_Inc, // 25
|
|
EvenData14, // 26
|
|
EvenData14_Inc, // 27
|
|
EvenData15, // 28
|
|
EvenData15_Inc, // 29
|
|
EvenData16, // 30
|
|
EvenData16_Inc, // 31
|
|
EvenData17, // 32
|
|
EvenData17_Inc, // 33
|
|
EvenData18, // 34
|
|
EvenData18_Inc, // 35
|
|
EvenData19, // 36
|
|
EvenData19_Inc, // 37
|
|
EvenData20, // 38
|
|
EvenData20_Inc, // 39
|
|
EvenData21, // 40
|
|
EvenData21_Inc, // 41
|
|
EvenData22, // 42
|
|
EvenData22_Inc, // 43
|
|
EvenData23, // 44
|
|
EvenData23_Inc, // 45
|
|
EvenData24, // 46
|
|
EvenData24_Inc, // 47
|
|
EvenData25, // 48
|
|
EvenData25_Inc, // 49
|
|
EvenData26, // 50
|
|
EvenData26_Inc, // 51
|
|
EvenData27, // 52
|
|
EvenData27_Inc, // 53
|
|
EvenData28, // 54
|
|
EvenData28_Inc, // 55
|
|
EvenData29, // 56
|
|
EvenData29_Inc, // 57
|
|
EvenData30, // 58
|
|
EvenData30_Inc, // 59
|
|
EvenData31, // 60
|
|
EvenData31_Inc, // 61
|
|
EvenData32, // 62
|
|
EvenData32_Inc, // 63
|
|
OddData1, // 64
|
|
OddData1_Inc, // 65
|
|
OddData2, // 66
|
|
OddData2_Inc, // 67
|
|
OddData3, // 68
|
|
OddData3_Inc, // 69
|
|
OddData4, // 70
|
|
OddData4_Inc, // 71
|
|
OddData5, // 72
|
|
OddData5_Inc, // 73
|
|
OddData6, // 74
|
|
OddData6_Inc, // 75
|
|
OddData7, // 76
|
|
OddData7_Inc, // 77
|
|
OddData8, // 78
|
|
OddData8_Inc, // 79
|
|
OddData9, // 80
|
|
OddData9_Inc, // 81
|
|
OddData10, // 82
|
|
OddData10_Inc, // 83
|
|
OddData11, // 84
|
|
OddData11_Inc, // 85
|
|
OddData12, // 86
|
|
OddData12_Inc, // 87
|
|
OddData13, // 88
|
|
OddData13_Inc, // 89
|
|
OddData14, // 90
|
|
OddData14_Inc, // 91
|
|
OddData15, // 92
|
|
OddData15_Inc, // 93
|
|
OddData16, // 94
|
|
OddData16_Inc, // 95
|
|
OddData17, // 96
|
|
OddData17_Inc, // 97
|
|
OddData18, // 98
|
|
OddData18_Inc, // 99
|
|
OddData19, // 100
|
|
OddData19_Inc, // 101
|
|
OddData20, // 102
|
|
OddData20_Inc, // 103
|
|
OddData21, // 104
|
|
OddData21_Inc, // 105
|
|
OddData22, // 106
|
|
OddData22_Inc, // 107
|
|
OddData23, // 108
|
|
OddData23_Inc, // 109
|
|
OddData24, // 110
|
|
OddData24_Inc, // 111
|
|
OddData25, // 112
|
|
OddData25_Inc, // 113
|
|
OddData26, // 114
|
|
OddData26_Inc, // 115
|
|
OddData27, // 116
|
|
OddData27_Inc, // 117
|
|
OddData28, // 118
|
|
OddData28_Inc, // 119
|
|
OddData29, // 120
|
|
OddData29_Inc, // 121
|
|
OddData30, // 122
|
|
OddData30_Inc, // 123
|
|
OddData31, // 124
|
|
OddData31_Inc, // 125
|
|
OddData32, // 126
|
|
OddData32_Inc, // 127
|
|
Side1, // 128
|
|
Side2, // 129
|
|
Side3, // 130
|
|
Side4, // 131
|
|
Side5, // 132
|
|
Side6, // 133
|
|
Side7, // 134
|
|
Side8, // 135
|
|
Side9, // 136
|
|
Side10, // 137
|
|
Side11, // 138
|
|
Side12, // 139
|
|
Side13, // 140
|
|
Side14, // 141
|
|
Side15, // 142
|
|
Side16, // 143
|
|
Side17, // 144
|
|
Side18, // 145
|
|
Side19, // 146
|
|
Side20, // 147
|
|
Side21, // 148
|
|
Side22, // 149
|
|
Side23, // 150
|
|
Side24, // 151
|
|
Side25, // 152
|
|
Side26, // 153
|
|
Side27, // 154
|
|
Side28, // 155
|
|
Side29, // 156
|
|
Side30, // 157
|
|
Side31, // 158
|
|
Side32, // 159
|
|
Shadow1, // 160
|
|
Shadow2, // 161
|
|
Shadow3, // 162
|
|
Shadow4, // 163
|
|
Shadow5, // 164
|
|
Shadow6, // 165
|
|
Shadow7, // 166
|
|
Shadow8, // 167
|
|
Shadow9, // 168
|
|
Shadow10, // 169
|
|
Shadow11, // 170
|
|
Shadow12, // 171
|
|
Shadow13, // 172
|
|
Shadow14, // 173
|
|
Shadow15, // 174
|
|
Shadow16, // 175
|
|
Shadow17, // 176
|
|
Shadow18, // 177
|
|
Shadow19, // 178
|
|
Shadow20, // 179
|
|
Shadow21, // 180
|
|
Shadow22, // 181
|
|
Shadow23, // 182
|
|
Shadow24, // 183
|
|
Shadow25, // 184
|
|
Shadow26, // 185
|
|
Shadow27, // 186
|
|
Shadow28, // 187
|
|
Shadow29, // 188
|
|
Shadow30, // 189
|
|
Shadow31, // 190
|
|
Shadow32, // 191
|
|
EvenDataLB, // 192
|
|
EvenDataLB_Inc, // 193
|
|
EvenDataLW, // 194
|
|
EvenDataLW_Inc, // 195
|
|
EvenDataLWB, // 196
|
|
EvenDataLWB_Inc, // 197
|
|
EvenDataL, // 198
|
|
EvenDataL_Inc, // 199
|
|
OddDataLB, // 200
|
|
OddDataLB_Inc, // 201
|
|
OddDataLW, // 202
|
|
OddDataLW_Inc, // 203
|
|
OddDataLWB, // 204
|
|
OddDataLWB_Inc, // 205
|
|
OddDataL, // 206
|
|
OddDataL_Inc, // 207
|
|
SideN, // 208
|
|
ShadowN, // 209
|
|
TransparentN, // 210
|
|
EndScan, // 211
|
|
Error, // 212
|
|
EvenDataStart = 0,
|
|
EvenDataEnd = 63,
|
|
OddDataStart = 64,
|
|
OddDataEnd = 127,
|
|
SideStart = 128,
|
|
SideEnd = 159,
|
|
ShadowStart = 160,
|
|
ShadowEnd = 191,
|
|
EvenDataNStart = 192,
|
|
EvenDataNEnd = 199,
|
|
OddDataNStart = 200,
|
|
OddDataNEnd = 207
|
|
};
|
|
|
|
enum OpType { None, Data, Side, Shadow, Transparent };
|
|
|
|
struct Frame {
|
|
public ArrayList alsArunsEven;
|
|
public ArrayList alsArunsOdd;
|
|
}
|
|
|
|
struct Run {
|
|
public int cp;
|
|
public ColorType ct;
|
|
public Align align;
|
|
public int[] aiclr;
|
|
public bool fNeedSrcAlign;
|
|
}
|
|
|
|
struct RunArgs {
|
|
public Op op;
|
|
public int cpSrc;
|
|
public int cpArgs;
|
|
public int cpDst;
|
|
}
|
|
|
|
class CompileResults {
|
|
public ArrayList alsIclr;
|
|
public ArrayList alsRa;
|
|
public int[,] aaiiclrEven;
|
|
public int[,] aairaEven;
|
|
public int[,] aaiiclrOdd;
|
|
public int[,] aairaOdd;
|
|
}
|
|
|
|
int m_cx;
|
|
int m_cy;
|
|
Palette m_pal;
|
|
ArrayList m_alsFrames = new ArrayList();
|
|
CompileResults m_crLast;
|
|
static Color m_clrShadow = Color.FromArgb(156, 212, 248);
|
|
static Color m_clrTransparent = Color.FromArgb(255, 0, 255);
|
|
static int s_iclrShadow = -1;
|
|
static int s_iclrTransparent = -2;
|
|
static int s_iclrNotAssigned = -3;
|
|
static int s_iclrSideFirst = 16;
|
|
static int s_iclrSideLast = 20;
|
|
static int s_cpSideOpFixedMax = 32;
|
|
static int s_cpShadowOpFixedMax = 32;
|
|
static int s_cpDataOpFixedMax = 32;
|
|
static Op[] s_mpcpTrailingOpDataEven = { Op.EvenDataL, Op.EvenDataLB, Op.EvenDataLW, Op.EvenDataLWB };
|
|
static Op[] s_mpcpTrailingOpDataOdd = { Op.OddDataL, Op.OddDataLB, Op.OddDataLW, Op.OddDataLWB };
|
|
|
|
public TBitmapSR(Palette pal) {
|
|
m_cx = -1;
|
|
m_cy = -1;
|
|
m_pal = pal;
|
|
}
|
|
|
|
public void Dispose() {
|
|
}
|
|
|
|
public void AddFrame(Bitmap bm) {
|
|
if (m_cx == -1) {
|
|
m_cx = bm.Width;
|
|
m_cy = bm.Height;
|
|
}
|
|
if (m_cx != bm.Width || m_cy != bm.Height)
|
|
throw new Exception("Bitmap different width and height!");
|
|
|
|
// Get color converted scanlines for this bitmap
|
|
int[][] aaiclrScans = GetScans(bm);
|
|
|
|
// Add ScanRecords for each scan
|
|
Frame frame = new Frame();
|
|
frame.alsArunsEven = new ArrayList();
|
|
frame.alsArunsOdd = new ArrayList();
|
|
for (int y = 0; y < m_cy; y++) {
|
|
frame.alsArunsEven.Add(GetRuns(Align.Even, aaiclrScans[y]));
|
|
frame.alsArunsOdd.Add(GetRuns(Align.Odd, aaiclrScans[y]));
|
|
}
|
|
|
|
// Add this frame
|
|
m_alsFrames.Add(frame);
|
|
}
|
|
|
|
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]);
|
|
|
|
// Transparent and shadow don't exist in the output palette
|
|
// so they have special values.
|
|
if (clr == m_clrTransparent) {
|
|
aiclrScan[x] = s_iclrTransparent;
|
|
continue;
|
|
}
|
|
if (clr == m_clrShadow) {
|
|
aiclrScan[x] = s_iclrShadow;
|
|
continue;
|
|
}
|
|
|
|
// Find closest entry in this palette
|
|
aiclrScan[x] = m_pal.FindClosestEntry(clr);
|
|
}
|
|
aaiclrScans[y] = aiclrScan;
|
|
}
|
|
bm.UnlockBits(bmd);
|
|
return aaiclrScans;
|
|
}
|
|
|
|
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;
|
|
|
|
// We've encountered a color of a different type.
|
|
// If we're leaving a side run or if we're entering a data run, then check if side colors are
|
|
// close ahead. If they are then enter a side run or stay in the existing side run for
|
|
// efficiency's sake.
|
|
if (ctCurrent == ColorType.Data) {
|
|
for (int iiclrT = iiclr + 1; iiclrT < iiclr + 6; iiclrT++) {
|
|
// If we've run out of pixels, continue the side run if that's what we're in
|
|
if (iiclrT >= aiclrScan.Length) {
|
|
if (ctRun == ColorType.Side)
|
|
ctCurrent = ColorType.Side;
|
|
break;
|
|
}
|
|
|
|
// If we encountered a side color, don't switch
|
|
ColorType ctT = GetColorType(aiclrScan[iiclrT]);
|
|
if (ctT == ColorType.Side) {
|
|
ctCurrent = ColorType.Side;
|
|
break;
|
|
}
|
|
|
|
// If we've hit something other than data, it's cheaper to stay with the side run than
|
|
// switch to a small data run.
|
|
if (ctT != ColorType.Data) {
|
|
if (ctRun == ColorType.Side)
|
|
ctCurrent = ColorType.Side;
|
|
break;
|
|
}
|
|
}
|
|
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);
|
|
Debug.Assert((int)alsCounts[alsCounts.Count - 1] <= 255);
|
|
}
|
|
|
|
// Start a run with this color type
|
|
iiclrFirst = iiclr;
|
|
ctRun = ctCurrent;
|
|
}
|
|
// Add the last run
|
|
alsCtRuns.Add(ctRun);
|
|
alsCounts.Add(aiclrScan.Length - iiclrFirst);
|
|
Debug.Assert((int)alsCounts[alsCounts.Count - 1] <= 255);
|
|
|
|
// 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++) {
|
|
if (i == alsCtRuns.Count - 1 && (ColorType)alsCtRuns[i] == ColorType.Transparent)
|
|
break;
|
|
AddRun(alsRun, alignScan, iiclrRun, aiclrScan, (int)alsCounts[i], (ColorType)alsCtRuns[i]);
|
|
iiclrRun += (int)alsCounts[i];
|
|
}
|
|
|
|
// Add terminator
|
|
Run run;
|
|
run.ct = ColorType.EndScan;
|
|
run.cp = 0;
|
|
run.align = Align.None;
|
|
run.aiclr = null;
|
|
run.fNeedSrcAlign = false;
|
|
alsRun.Add(run);
|
|
|
|
// 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:
|
|
// Always aligned; needed to ensure word / dword copies
|
|
alignRun = alignDst;
|
|
goto CopyRun;
|
|
|
|
case ColorType.Side:
|
|
CopyRun:
|
|
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;
|
|
run.fNeedSrcAlign = false;
|
|
alsRun.Add(run);
|
|
}
|
|
|
|
struct RunDataInstance {
|
|
public Run[] arun;
|
|
public int[] aiclr;
|
|
public Align align;
|
|
}
|
|
|
|
RunDataInstance[] PermuteRunData(Run[] arun, int cMax) {
|
|
ArrayList alsRdInst = new ArrayList();
|
|
Permute2(Align.Even, 0, arun, new int[0], alsRdInst, cMax);
|
|
Permute2(Align.Odd, 0, arun, new int[0], alsRdInst, cMax);
|
|
return (RunDataInstance[])alsRdInst.ToArray(typeof(RunDataInstance));
|
|
}
|
|
|
|
void Permute2(Align align, int irun, Run[] arun, int[] aiclr, ArrayList alsRdInst, int cMax) {
|
|
if (irun >= arun.Length) {
|
|
RunDataInstance rdinst;
|
|
rdinst.arun = (Run[])arun.Clone();
|
|
rdinst.aiclr = (int[])aiclr.Clone();
|
|
rdinst.align = align;
|
|
alsRdInst.Add(rdinst);
|
|
return;
|
|
}
|
|
|
|
// Determine arguments
|
|
RunArgs ra = GetRunArg(arun[irun]);
|
|
int nArg = GetCountArg(ra);
|
|
if (arun[irun].align != Align.None) {
|
|
// Forced alignment
|
|
ArrayList alsIclr = new ArrayList();
|
|
alsIclr.AddRange(aiclr);
|
|
Align alignDst;
|
|
if (align == Align.Even) {
|
|
alignDst = (aiclr.Length & 1) != 0 ? Align.Odd : Align.Even;
|
|
} else {
|
|
alignDst = (aiclr.Length & 1) == 0 ? Align.Odd : Align.Even;
|
|
}
|
|
arun[irun].fNeedSrcAlign = false;
|
|
if (nArg != -1) {
|
|
if (alignDst == arun[irun].align) {
|
|
arun[irun].fNeedSrcAlign = true;
|
|
alsIclr.Add(s_iclrNotAssigned);
|
|
}
|
|
alsIclr.Add(nArg);
|
|
} else {
|
|
if (alignDst != arun[irun].align) {
|
|
arun[irun].fNeedSrcAlign = true;
|
|
alsIclr.Add(s_iclrNotAssigned);
|
|
}
|
|
}
|
|
alsIclr.AddRange(arun[irun].aiclr);
|
|
Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax);
|
|
return;
|
|
} else {
|
|
// No adjust
|
|
ArrayList alsIclr = new ArrayList();
|
|
alsIclr.AddRange(aiclr);
|
|
if (nArg != -1)
|
|
alsIclr.Add(nArg);
|
|
if (arun[irun].aiclr != null)
|
|
alsIclr.AddRange(arun[irun].aiclr);
|
|
arun[irun].fNeedSrcAlign = false;
|
|
Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax);
|
|
if (cMax != -1 && alsRdInst.Count >= cMax)
|
|
return;
|
|
|
|
#if false
|
|
//This saves a modest amount of space however currently we're not supporting _Incs of all ops.
|
|
// With adjust
|
|
alsIclr = new ArrayList();
|
|
alsIclr.AddRange(aiclr);
|
|
alsIclr.Add(s_iclrNotAssigned);
|
|
if (fCountArg)
|
|
alsIclr.Add(arun[irun].cp - 1);
|
|
arun[irun].fNeedSrcAlign = true;
|
|
if (arun[irun].aiclr != null)
|
|
alsIclr.AddRange(arun[irun].aiclr);
|
|
Permute2(align, irun + 1, arun, (int[])alsIclr.ToArray(typeof(int)), alsRdInst, cMax);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int CompileImageData(ArrayList alsIclr, Run[] arun, bool fSmallest) {
|
|
// Get all permutations possible of run data
|
|
RunDataInstance[] ardinst = PermuteRunData(arun, fSmallest ? -1 : 1);
|
|
|
|
// Match the best permutation with existing data
|
|
int iiclrStart;
|
|
int ird = AddBestRunDataInstance(alsIclr, ardinst, out iiclrStart);
|
|
RunDataInstance rdinst = ardinst[ird];
|
|
|
|
// Patch the passed arun with the matched run
|
|
for (int irun = 0; irun < arun.Length; irun++)
|
|
arun[irun] = rdinst.arun[irun];
|
|
|
|
// Return where the data starts
|
|
//temp
|
|
// Debug.Assert(iiclrStart == MatchImageData(rdinst.align, rdinst.aiclr, alsIclr));
|
|
return iiclrStart;
|
|
}
|
|
|
|
int AddBestRunDataInstance(ArrayList alsIclr, RunDataInstance[] ardinst, out int iiclrStart) {
|
|
// Match against all permutations
|
|
int irdLowest = 0;
|
|
int iiclrLowest = alsIclr.Count;
|
|
for (int ird = 0; ird < ardinst.Length; ird++) {
|
|
RunDataInstance rdinst = ardinst[ird];
|
|
int iiclrMatch = MatchImageData(rdinst.align, rdinst.aiclr, alsIclr);
|
|
if (iiclrMatch < iiclrLowest) {
|
|
iiclrLowest = iiclrMatch;
|
|
irdLowest = ird;
|
|
}
|
|
}
|
|
|
|
// Contained or partial match
|
|
if (iiclrLowest < alsIclr.Count) {
|
|
// Patch s_iclrNotAssigned in dst since we can reuse them
|
|
RunDataInstance rdinst = ardinst[irdLowest];
|
|
int iiclr = 0;
|
|
for (; iiclr < rdinst.aiclr.Length && iiclr + iiclrLowest < alsIclr.Count; iiclr++) {
|
|
if ((int)alsIclr[iiclrLowest + iiclr] == s_iclrNotAssigned)
|
|
alsIclr[iiclrLowest + iiclr] = rdinst.aiclr[iiclr];
|
|
Debug.Assert(rdinst.aiclr[iiclr] == s_iclrNotAssigned || rdinst.aiclr[iiclr] == (int)alsIclr[iiclrLowest + iiclr]);
|
|
}
|
|
|
|
// Add the rest if needed
|
|
for (; iiclr < rdinst.aiclr.Length; iiclr++) {
|
|
Debug.Assert(iiclrLowest + iiclr == alsIclr.Count);
|
|
alsIclr.Add(rdinst.aiclr[iiclr]);
|
|
}
|
|
iiclrStart = iiclrLowest;
|
|
return irdLowest;
|
|
}
|
|
|
|
// No best match; add the smallest
|
|
int irdSmallest = 0;
|
|
int cbSmallest = 0x7fff;
|
|
Align alignDst = (alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even;
|
|
for (int ird = 0; ird < ardinst.Length; ird++) {
|
|
RunDataInstance rdinst = ardinst[ird];
|
|
int cbT = rdinst.aiclr.Length;
|
|
|
|
//temp
|
|
#if true
|
|
if (alignDst != rdinst.align)
|
|
continue;
|
|
#endif
|
|
|
|
if (alignDst != rdinst.align)
|
|
cbT++;
|
|
if (cbT < cbSmallest) {
|
|
irdSmallest = ird;
|
|
cbSmallest = cbT;
|
|
}
|
|
}
|
|
|
|
// Add the data
|
|
if (alignDst != ardinst[irdSmallest].align)
|
|
alsIclr.Add(s_iclrNotAssigned);
|
|
int iiclrDst = alsIclr.Count;
|
|
alsIclr.AddRange(ardinst[irdSmallest].aiclr);
|
|
iiclrStart = iiclrDst;
|
|
return irdSmallest;
|
|
}
|
|
|
|
int MatchImageData(Align align, int[] aiclr, ArrayList alsIclr) {
|
|
//temp
|
|
#if false
|
|
// Find a full or partial (off the end) match
|
|
bool fMatch = false;
|
|
for (int iiclrDst = 0; iiclrDst < alsIclr.Count; iiclrDst++) {
|
|
// Proper alignment?
|
|
Align alignDst = (iiclrDst & 1) != 0 ? Align.Odd : Align.Even;
|
|
if (align != alignDst)
|
|
continue;
|
|
|
|
// Match
|
|
fMatch = true;
|
|
for (int iiclrSrc = 0; iiclrSrc < aiclr.Length; iiclrSrc++) {
|
|
// Partial match?
|
|
int iiclrDstT = iiclrDst + iiclrSrc;
|
|
if (iiclrDstT >= alsIclr.Count)
|
|
return iiclrDst;
|
|
|
|
// Continue matching
|
|
if (aiclr[iiclrSrc] == s_iclrNotAssigned)
|
|
continue;
|
|
if ((int)alsIclr[iiclrDstT] == s_iclrNotAssigned)
|
|
continue;
|
|
if (aiclr[iiclrSrc] == (int)alsIclr[iiclrDstT])
|
|
continue;
|
|
fMatch = false;
|
|
break;
|
|
}
|
|
if (fMatch)
|
|
return iiclrDst;
|
|
}
|
|
|
|
#endif
|
|
// No match
|
|
return alsIclr.Count;
|
|
}
|
|
|
|
int GetCountArg(RunArgs ra) {
|
|
switch (ra.op) {
|
|
case Op.EvenDataLB:
|
|
case Op.EvenDataLB_Inc:
|
|
Debug.Assert(((ra.cpDst - 1) & 3) == 0);
|
|
return (63 - (ra.cpDst - 1) / 4) * 2;
|
|
|
|
case Op.EvenDataLW:
|
|
case Op.EvenDataLW_Inc:
|
|
Debug.Assert(((ra.cpDst - 2) & 3) == 0);
|
|
return (63 - (ra.cpDst - 2) / 4) * 2;
|
|
|
|
case Op.EvenDataLWB:
|
|
case Op.EvenDataLWB_Inc:
|
|
Debug.Assert(((ra.cpDst - 3) & 3) == 0);
|
|
return (63 - (ra.cpDst - 3) / 4) * 2;
|
|
|
|
case Op.EvenDataL:
|
|
case Op.EvenDataL_Inc:
|
|
Debug.Assert(((ra.cpDst - 0) & 3) == 0);
|
|
return (64 - (ra.cpDst - 0) / 4) * 2;
|
|
|
|
case Op.OddDataLB:
|
|
case Op.OddDataLB_Inc:
|
|
Debug.Assert(((-1 + ra.cpDst - 1) & 3) == 0);
|
|
return (63 - (-1 + ra.cpDst - 1) / 4) * 2;
|
|
|
|
case Op.OddDataLW:
|
|
case Op.OddDataLW_Inc:
|
|
Debug.Assert(((-1 + ra.cpDst - 2) & 3) == 0);
|
|
return (63 - (-1 + ra.cpDst - 2) / 4) * 2;
|
|
|
|
case Op.OddDataLWB:
|
|
case Op.OddDataLWB_Inc:
|
|
Debug.Assert(((-1 + ra.cpDst - 3) & 3) == 0);
|
|
return (63 - (-1 + ra.cpDst - 3) / 4) * 2;
|
|
|
|
case Op.OddDataL:
|
|
case Op.OddDataL_Inc:
|
|
Debug.Assert(((-1 + ra.cpDst - 0) & 3) == 0);
|
|
return (63 - (-1 + ra.cpDst - 0) / 4) * 2;
|
|
|
|
case Op.SideN:
|
|
case Op.ShadowN:
|
|
Debug.Assert(ra.cpDst < 64);
|
|
return (64 - ra.cpDst) * 6 / 2;
|
|
|
|
case Op.TransparentN:
|
|
return ra.cpDst;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RunArgs GetRunArg(Run run) {
|
|
// Figure out the appropriate op for this run
|
|
Op op = Op.Error;
|
|
switch (run.ct) {
|
|
case ColorType.Transparent:
|
|
op = Op.TransparentN;
|
|
Debug.Assert(run.aiclr == null);
|
|
break;
|
|
|
|
case ColorType.Side:
|
|
if (run.cp <= s_cpSideOpFixedMax) {
|
|
op = Op.SideStart + run.cp - 1;
|
|
} else {
|
|
op = Op.SideN;
|
|
}
|
|
Debug.Assert(run.aiclr != null);
|
|
Debug.Assert(run.aiclr.Length == run.cp);
|
|
break;
|
|
|
|
case ColorType.Shadow:
|
|
if (run.cp <= s_cpShadowOpFixedMax) {
|
|
op = Op.ShadowStart + run.cp - 1;
|
|
} else {
|
|
op = Op.ShadowN;
|
|
}
|
|
Debug.Assert(run.aiclr == null);
|
|
break;
|
|
|
|
case ColorType.Data:
|
|
if (run.cp <= s_cpDataOpFixedMax) {
|
|
if (run.align == Align.Even) {
|
|
op = (Op)((int)Op.EvenDataStart + (run.cp - 1) * 2 + (run.fNeedSrcAlign ? 1 : 0));
|
|
} else if (run.align == Align.Odd) {
|
|
op = (Op)((int)Op.OddDataStart + (run.cp - 1) * 2 + (run.fNeedSrcAlign ? 1 : 0));
|
|
} else {
|
|
Debug.Assert(false);
|
|
}
|
|
} else {
|
|
if (run.align == Align.Even) {
|
|
op = s_mpcpTrailingOpDataEven[run.aiclr.Length % 4];
|
|
} else {
|
|
op = s_mpcpTrailingOpDataOdd[(run.aiclr.Length - 1)% 4];
|
|
}
|
|
op += (run.fNeedSrcAlign ? 1 : 0);
|
|
}
|
|
Debug.Assert(run.aiclr != null);
|
|
Debug.Assert(run.aiclr.Length == run.cp);
|
|
break;
|
|
|
|
case ColorType.EndScan:
|
|
op = Op.EndScan;
|
|
Debug.Assert(run.aiclr == null);
|
|
break;
|
|
|
|
default:
|
|
op = Op.Error;
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
|
|
// Make RunArgs
|
|
RunArgs ra;
|
|
ra.op = op;
|
|
ra.cpDst = run.cp;
|
|
ra.cpSrc = 0;
|
|
ra.cpArgs = 0;
|
|
if (run.aiclr != null)
|
|
ra.cpSrc += run.aiclr.Length;
|
|
if (run.fNeedSrcAlign) {
|
|
ra.cpArgs++;
|
|
ra.cpSrc++;
|
|
}
|
|
if (GetCountArg(ra) != -1) {
|
|
ra.cpSrc++;
|
|
ra.cpArgs++;
|
|
}
|
|
return ra;
|
|
}
|
|
|
|
RunArgs[] MakeRunArgs(Run[] arun) {
|
|
ArrayList alsRa = new ArrayList();
|
|
foreach (Run run in arun)
|
|
alsRa.Add(GetRunArg(run));
|
|
return (RunArgs[])alsRa.ToArray(typeof(RunArgs));
|
|
}
|
|
|
|
int CompileRunArgs(ArrayList alsRa, Run[] arun) {
|
|
RunArgs[] ara = MakeRunArgs(arun);
|
|
|
|
//temp
|
|
#if false
|
|
// Incorporate the run args into the list. Match against existing args if possible
|
|
bool fMatch = false;
|
|
int iraDst = 0;
|
|
for (; iraDst < alsRa.Count; iraDst++) {
|
|
fMatch = true;
|
|
for (int iraSrc = 0; iraSrc < ara.Length; iraSrc++) {
|
|
int iraDstT = iraSrc + iraDst;
|
|
if (iraDstT >= alsRa.Count) {
|
|
for (; iraSrc < ara.Length; iraSrc++)
|
|
alsRa.Add(ara[iraSrc]);
|
|
fMatch = true;
|
|
break;
|
|
}
|
|
RunArgs raSrc = ara[iraSrc];
|
|
RunArgs raDst = (RunArgs)alsRa[iraDstT];
|
|
if (raSrc.cpDst != raDst.cpDst || raSrc.op != raDst.op) {
|
|
fMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
if (fMatch)
|
|
break;
|
|
}
|
|
if (!fMatch)
|
|
alsRa.AddRange(ara);
|
|
|
|
return iraDst;
|
|
#else
|
|
int iraDst = alsRa.Count;
|
|
alsRa.AddRange(ara);
|
|
return iraDst;
|
|
#endif
|
|
}
|
|
|
|
public void Compile(bool fSmallest) {
|
|
CompileResults cr = new CompileResults();
|
|
cr.alsIclr = new ArrayList();
|
|
cr.alsRa = new ArrayList();
|
|
cr.aaiiclrEven = new int[m_alsFrames.Count, m_cy];
|
|
cr.aairaEven = new int[m_alsFrames.Count, m_cy];
|
|
cr.aaiiclrOdd = new int[m_alsFrames.Count, m_cy];
|
|
cr.aairaOdd = new int[m_alsFrames.Count, m_cy];
|
|
for (int iFrame = 0; iFrame < m_alsFrames.Count; iFrame++) {
|
|
Frame frame = (Frame)m_alsFrames[iFrame];
|
|
for (int y = 0; y < m_cy; y++) {
|
|
cr.aaiiclrEven[iFrame, y] = CompileImageData(cr.alsIclr, (Run[])frame.alsArunsEven[y], fSmallest);
|
|
cr.aairaEven[iFrame, y] = CompileRunArgs(cr.alsRa, (Run[])frame.alsArunsEven[y]);
|
|
}
|
|
for (int y = 0; y < m_cy; y++) {
|
|
cr.aaiiclrOdd[iFrame, y] = CompileImageData(cr.alsIclr, (Run[])frame.alsArunsOdd[y], fSmallest);
|
|
cr.aairaOdd[iFrame, y] = CompileRunArgs(cr.alsRa, (Run[])frame.alsArunsOdd[y]);
|
|
}
|
|
}
|
|
m_crLast = cr;
|
|
}
|
|
|
|
byte[] Serialize() {
|
|
|
|
// word type;
|
|
//struct TBitmapSRHeader {
|
|
// word cx;
|
|
// word cy;
|
|
// word cra;
|
|
// word ibra;
|
|
// word cFrames;
|
|
// Frame aframe[1];
|
|
//};
|
|
|
|
if (m_crLast == null)
|
|
throw new Exception("Compile image first!");
|
|
CompileResults cr = m_crLast;
|
|
int ibSrStart = 2 + 2 + 2 + 2 + 2 + 4 * m_alsFrames.Count;
|
|
int cbSr = m_alsFrames.Count * (m_cy * 4 + m_cy * 4);
|
|
int ibRaStart = ibSrStart + cbSr;
|
|
int cbRa = cr.alsRa.Count * 4;
|
|
int ibDataStart = ibRaStart + cbRa;
|
|
|
|
// Write header info
|
|
BinaryWriter bwtr = new BinaryWriter(new MemoryStream());
|
|
bwtr.Write(Misc.SwapUShort((ushort)TbmType.SkipRun)); // not counted in offsets
|
|
bwtr.Write(Misc.SwapUShort((ushort)m_cx));
|
|
bwtr.Write(Misc.SwapUShort((ushort)m_cy));
|
|
bwtr.Write(Misc.SwapUShort((ushort)cr.alsRa.Count));
|
|
bwtr.Write(Misc.SwapUShort((ushort)ibRaStart));
|
|
bwtr.Write(Misc.SwapUShort((ushort)m_alsFrames.Count));
|
|
|
|
// Write isrEven, isrOdd for each frame
|
|
int isr = 0;
|
|
foreach (Frame frame in m_alsFrames) {
|
|
bwtr.Write(Misc.SwapUShort((ushort)(isr + ibSrStart)));
|
|
isr += frame.alsArunsEven.Count * 4;
|
|
bwtr.Write(Misc.SwapUShort((ushort)(isr + ibSrStart)));
|
|
isr += frame.alsArunsOdd.Count * 4;
|
|
}
|
|
|
|
// Write sr's (ScanRecords) for each frame
|
|
for (int iFrame = 0; iFrame < m_alsFrames.Count; iFrame++) {
|
|
Frame frame = (Frame)m_alsFrames[iFrame];
|
|
for (int y = 0; y < m_cy; y++) {
|
|
long pos = bwtr.BaseStream.Position - 2;
|
|
bwtr.Write(Misc.SwapUShort((ushort)(cr.aaiiclrEven[iFrame, y] + ibDataStart - pos)));
|
|
bwtr.Write(Misc.SwapUShort((ushort)(cr.aairaEven[iFrame, y] * 4)));
|
|
}
|
|
for (int y = 0; y < m_cy; y++) {
|
|
long pos = bwtr.BaseStream.Position - 2;
|
|
bwtr.Write(Misc.SwapUShort((ushort)(cr.aaiiclrOdd[iFrame, y] + ibDataStart - pos)));
|
|
bwtr.Write(Misc.SwapUShort((ushort)(cr.aairaOdd[iFrame, y] * 4)));
|
|
}
|
|
}
|
|
|
|
// Write Ras
|
|
foreach (RunArgs ra in cr.alsRa) {
|
|
bwtr.Write((byte)ra.op);
|
|
bwtr.Write((byte)ra.cpSrc);
|
|
bwtr.Write((byte)ra.cpArgs);
|
|
bwtr.Write((byte)ra.cpDst);
|
|
}
|
|
|
|
// Write iclrData
|
|
foreach (int iclr in cr.alsIclr)
|
|
bwtr.Write((byte)iclr);
|
|
|
|
// Done
|
|
byte[] ab = new Byte[bwtr.BaseStream.Length];
|
|
bwtr.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
bwtr.BaseStream.Read(ab, 0, ab.Length);
|
|
bwtr.Close();
|
|
return ab;
|
|
}
|
|
|
|
#if false
|
|
// From runs version
|
|
public Bitmap CreateBitmap(int iFrame, bool fEven, bool fAsRuns) {
|
|
Bitmap bm = new Bitmap(m_cx, m_cy);
|
|
Graphics gMem = Graphics.FromImage(bm);
|
|
gMem.Clear(Color.FromArgb(0, 255, 255));
|
|
|
|
Frame frame = (Frame)m_alsFrames[iFrame];
|
|
ArrayList alsArun = fEven ? frame.alsArunsEven : frame.alsArunsOdd;
|
|
|
|
int y = 0;
|
|
int nColor = 0;
|
|
foreach(Run[] arun in alsArun) {
|
|
int x = 0;
|
|
foreach (Run run in arun) {
|
|
// Next run
|
|
if (run.ct == ColorType.EndScan) {
|
|
y++;
|
|
break;
|
|
}
|
|
|
|
// Transparency
|
|
if (run.ct == ColorType.Transparent) {
|
|
for (int xT = x; xT < x + run.cp; xT++)
|
|
bm.SetPixel(xT, y, Color.FromArgb(0, 255, 255));
|
|
x += run.cp;
|
|
continue;
|
|
}
|
|
|
|
// Colorize
|
|
if (fAsRuns) {
|
|
nColor ^= 1;
|
|
Color clr = nColor != 0 ? Color.FromArgb(255, 0, 0) : Color.FromArgb(0, 0, 255);
|
|
for (int xT = x; xT < x + run.cp; xT++)
|
|
bm.SetPixel(xT, y, clr);
|
|
x += run.cp;
|
|
} else {
|
|
switch (run.ct) {
|
|
case ColorType.Data:
|
|
case ColorType.Side:
|
|
for (int xT = x; xT < x + run.cp; xT++)
|
|
bm.SetPixel(xT, y, m_pal[run.aiclr[xT - x]]);
|
|
x += run.cp;
|
|
break;
|
|
|
|
case ColorType.Shadow:
|
|
for (int xT = x; xT < x + run.cp; xT++)
|
|
bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0)); // Color.FromArgb(156, 212, 248));
|
|
x += run.cp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gMem.Dispose();
|
|
return bm;
|
|
}
|
|
#else
|
|
// From compiled version
|
|
public Bitmap CreateBitmap(int iFrame, bool fEven, bool fAsRuns) {
|
|
if (m_crLast == null)
|
|
throw new Exception("Compile image first!");
|
|
CompileResults cr = m_crLast;
|
|
|
|
Bitmap bm = new Bitmap(m_cx, m_cy);
|
|
Graphics gMem = Graphics.FromImage(bm);
|
|
gMem.Clear(Color.FromArgb(0, 255, 255));
|
|
|
|
Frame frame = (Frame)m_alsFrames[iFrame];
|
|
int[,] aaiiclr = fEven ? cr.aaiiclrEven : cr.aaiiclrOdd;
|
|
int[,] aaira = fEven ? cr.aairaEven : cr.aairaOdd;
|
|
|
|
int xOrigin = fEven ? 0 : 1;
|
|
int nColor = 0;
|
|
for (int y = 0; y < m_cy; y++) {
|
|
int x = 0;
|
|
int iiclr = aaiiclr[iFrame, y];
|
|
int irun = aaira[iFrame, y];
|
|
while (true) {
|
|
RunArgs ra = (RunArgs)cr.alsRa[irun++];
|
|
if (ra.op == Op.EndScan)
|
|
break;
|
|
|
|
// Colorize
|
|
if (fAsRuns) {
|
|
nColor ^= 1;
|
|
Color clr = nColor != 0 ? Color.FromArgb(255, 0, 0) : Color.FromArgb(0, 0, 255);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, clr);
|
|
x += ra.cpDst;
|
|
} else {
|
|
// Transparency
|
|
if (ra.op == Op.TransparentN) {
|
|
Debug.Assert((int)cr.alsIclr[iiclr++] == ra.cpDst);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, Color.FromArgb(0, 255, 255));
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op >= Op.EvenDataStart && ra.op <= Op.EvenDataEnd) {
|
|
Debug.Assert(((int)ra.op - (int)Op.EvenDataStart) / 2 + 1 == ra.cpDst);
|
|
if (((int)ra.op & 1) != 0)
|
|
iiclr++;
|
|
Debug.Assert(((x + xOrigin) & 1) == 0 && (iiclr & 1) == 0);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]);
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op >= Op.OddDataStart && ra.op <= Op.OddDataEnd) {
|
|
Debug.Assert(((int)ra.op - (int)Op.OddDataStart) / 2 + 1 == ra.cpDst);
|
|
if (((int)ra.op & 1) != 0)
|
|
iiclr++;
|
|
Debug.Assert(((x + xOrigin) & 1) != 0 && (iiclr & 1) != 0);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]);
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op >= Op.EvenDataNStart && ra.op <= Op.OddDataNEnd) {
|
|
if (((int)ra.op & 1) != 0)
|
|
iiclr++;
|
|
iiclr++;
|
|
Debug.Assert(((x + xOrigin) & 1) == (iiclr & 1));
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]);
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op >= Op.SideStart && ra.op <= Op.SideEnd) {
|
|
Debug.Assert((int)ra.op - (int)Op.Side1 + 1 == ra.cpDst);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]);
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op == Op.SideN) {
|
|
Debug.Assert((int)cr.alsIclr[iiclr++] == ra.cpDst);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, m_pal[(int)cr.alsIclr[iiclr++]]);
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op >= Op.ShadowStart && ra.op <= Op.ShadowEnd) {
|
|
Debug.Assert((int)ra.op - (int)Op.Shadow1 + 1 == ra.cpDst);
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0));
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
if (ra.op == Op.ShadowN) {
|
|
for (int xT = x; xT < x + ra.cpDst; xT++)
|
|
bm.SetPixel(xT, y, Color.FromArgb(255, 255, 0));
|
|
x += ra.cpDst;
|
|
continue;
|
|
}
|
|
Debug.Assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
gMem.Dispose();
|
|
return bm;
|
|
}
|
|
#endif
|
|
|
|
public void PrintStats() {
|
|
if (m_crLast == null)
|
|
throw new Exception("Compile image first!");
|
|
CompileResults cr = m_crLast;
|
|
|
|
int cRuns = 0;
|
|
int cSkips = 0;
|
|
int cRunsInScan = 0;
|
|
int cScans = 0;
|
|
foreach (Frame frame in m_alsFrames) {
|
|
foreach (Run[] arun in frame.alsArunsEven) {
|
|
cScans++;
|
|
foreach (Run run in arun) {
|
|
cRunsInScan++;
|
|
cRuns++;
|
|
if (run.ct == ColorType.Transparent)
|
|
cSkips++;
|
|
}
|
|
}
|
|
foreach (Run[] arun in frame.alsArunsOdd) {
|
|
cScans++;
|
|
foreach (Run run in arun) {
|
|
cRunsInScan++;
|
|
cRuns++;
|
|
if (run.ct == ColorType.Transparent)
|
|
cSkips++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("Runs: " + cRuns);
|
|
Console.WriteLine("Skips: " + cSkips);
|
|
Console.WriteLine("Avg runs per image: " + (float)cRuns / 2.0 / (float)m_alsFrames.Count);
|
|
Console.WriteLine("Avg runs per scan: " + (float)cRunsInScan / (float)cScans);
|
|
|
|
Console.WriteLine("RunArgs size: " + cr.alsRa.Count * 4);
|
|
Console.WriteLine("RunData size: " + cr.alsIclr.Count);
|
|
Console.WriteLine("SR size: " + m_cy * 4 * m_alsFrames.Count * 2);
|
|
}
|
|
|
|
public void Save(string strFile) {
|
|
if (m_crLast == null)
|
|
throw new Exception("Compile image first!");
|
|
BinaryWriter bwtr = new BinaryWriter(new FileStream(strFile, FileMode.Create, FileAccess.Write));
|
|
bwtr.Write(Serialize());
|
|
bwtr.Close();
|
|
}
|
|
|
|
public static void EmitOpEnum() {
|
|
TextWriter tw = Console.Out;
|
|
tw.WriteLine("enum Op {");
|
|
int iop = 0;
|
|
int iopEvenDataStart = iop;
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) {
|
|
tw.WriteLine("\tEvenData" + cp + ", // " + iop++);
|
|
tw.WriteLine("\tEvenData" + cp + "_Inc, // " + iop++);
|
|
}
|
|
int iopEvenDataEnd = iop - 1;
|
|
int iopOddDataStart = iop;
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++) {
|
|
tw.WriteLine("\tOddData" + cp + ", // " + iop++);
|
|
tw.WriteLine("\tOddData" + cp + "_Inc, // " + iop++);
|
|
}
|
|
int iopOddDataEnd = iop - 1;
|
|
int iopSideStart = iop;
|
|
for (int cp = 1; cp <= s_cpSideOpFixedMax; cp++) {
|
|
tw.WriteLine("\tSide" + cp + ", // " + iop++);
|
|
//tw.WriteLine("\tSide" + cp + "_Inc, // " + iop++);
|
|
}
|
|
int iopSideEnd = iop - 1;
|
|
int iopShadowStart = iop;
|
|
for (int cp = 1; cp <= s_cpShadowOpFixedMax; cp++) {
|
|
tw.WriteLine("\tShadow" + cp + ", // " + iop++);
|
|
//tw.WriteLine("\tShadow" + cp + "_Inc, // " + iop++);
|
|
}
|
|
int iopShadowEnd = iop - 1;
|
|
|
|
tw.WriteLine("\tEvenDataLB, // " + iop++);
|
|
tw.WriteLine("\tEvenDataLB_Inc, // " + iop++);
|
|
tw.WriteLine("\tEvenDataLW, // " + iop++);
|
|
tw.WriteLine("\tEvenDataLW_Inc, // " + iop++);
|
|
tw.WriteLine("\tEvenDataLWB, // " + iop++);
|
|
tw.WriteLine("\tEvenDataLWB_Inc, // " + iop++);
|
|
tw.WriteLine("\tEvenDataL, // " + iop++);
|
|
tw.WriteLine("\tEvenDataL_Inc, // " + iop++);
|
|
tw.WriteLine("\tOddDataLB, // " + iop++);
|
|
tw.WriteLine("\tOddDataLB_Inc, // " + iop++);
|
|
tw.WriteLine("\tOddDataLW, // " + iop++);
|
|
tw.WriteLine("\tOddDataLW_Inc, // " + iop++);
|
|
tw.WriteLine("\tOddDataLWB, // " + iop++);
|
|
tw.WriteLine("\tOddDataLWB_Inc, // " + iop++);
|
|
tw.WriteLine("\tOddDataL, // " + iop++);
|
|
tw.WriteLine("\tOddDataL_Inc, // " + iop++);
|
|
tw.WriteLine("\tSideN, // " + iop++);
|
|
//tw.WriteLine("\tSideN_Inc, // " + iop++);
|
|
tw.WriteLine("\tShadowN, // " + iop++);
|
|
//tw.WriteLine("\tShadowN_Inc, // " + iop++);
|
|
tw.WriteLine("\tTransparentN, // " + iop++);
|
|
//tw.WriteLine("\tTransparentN_Inc, // " + iop++);
|
|
tw.WriteLine("\tEndScan, // " + iop++);
|
|
tw.WriteLine("\tError, // " + iop++);
|
|
|
|
tw.WriteLine("\tEvenDataStart = " + iopEvenDataStart + ",");
|
|
tw.WriteLine("\tEvenDataEnd = " + iopEvenDataEnd + ",");
|
|
tw.WriteLine("\tOddDataStart = " + iopOddDataStart + ",");
|
|
tw.WriteLine("\tOddDataEnd = " + iopOddDataEnd + ",");
|
|
tw.WriteLine("\tSideStart = " + iopSideStart + ",");
|
|
tw.WriteLine("\tSideEnd = " + iopSideEnd + ",");
|
|
tw.WriteLine("\tShadowStart = " + iopShadowStart + ",");
|
|
tw.WriteLine("\tShadowEnd = " + iopShadowEnd);
|
|
|
|
tw.WriteLine("};");
|
|
tw.Close();
|
|
}
|
|
|
|
public static void EmitHandlers() {
|
|
//TextWriter tw = new StreamWriter(new MemoryStream());
|
|
TextWriter tw = Console.Out;
|
|
StringCollection strc = new StringCollection();
|
|
|
|
//tw.WriteLine(".text");
|
|
tw.WriteLine("");
|
|
|
|
// Fixed data handlers
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++)
|
|
EmitDataHandler(tw, cp, Align.Even, false, strc);
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++)
|
|
EmitDataHandler(tw, cp, Align.Odd, false, strc);
|
|
|
|
// Clipped fixed data handlers
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++)
|
|
EmitDataHandler(tw, cp, Align.Even, true, strc);
|
|
for (int cp = 1; cp <= s_cpDataOpFixedMax; cp++)
|
|
EmitDataHandler(tw, cp, Align.Odd, true, strc);
|
|
|
|
// Fixed side handlers
|
|
for (int cp = 1; cp <= s_cpSideOpFixedMax; cp++)
|
|
EmitSideHandler(tw, cp, strc);
|
|
// Fixed shadow handlers
|
|
for (int cp = 1; cp <= s_cpShadowOpFixedMax; cp++)
|
|
EmitShadowHandler(tw, cp, strc);
|
|
|
|
// Non-fixed data handlers
|
|
EmitDataNHandler(tw, "EvenDataLB", Align.Even, 63 * 4 + 1, strc);
|
|
EmitDataNHandler(tw, "EvenDataLW", Align.Even, 63 * 4 + 2, strc);
|
|
EmitDataNHandler(tw, "EvenDataLWB", Align.Even, 63 * 4 + 2 + 1, strc);
|
|
EmitDataNHandler(tw, "EvenDataL", Align.Even, 64 * 4, strc);
|
|
EmitDataNHandler(tw, "OddDataLB", Align.Odd, 1 + 63 * 4 + 1, strc);
|
|
EmitDataNHandler(tw, "OddDataLW", Align.Odd, 1 + 63 * 4 + 2, strc);
|
|
EmitDataNHandler(tw, "OddDataLWB", Align.Odd, 1 + 63 * 4 + 2 + 1, strc);
|
|
EmitDataNHandler(tw, "OddDataL", Align.Odd, 1 + 63 * 4, strc);
|
|
|
|
// Table
|
|
strc.Add("SideN");
|
|
strc.Add("ShadowN");
|
|
strc.Add("TransparentN");
|
|
strc.Add("EndScan");
|
|
tw.WriteLine(".data");
|
|
tw.WriteLine(".even");
|
|
tw.WriteLine(".globl gapfnRunOps");
|
|
tw.WriteLine("gapfnRunOps:");
|
|
for (int i = 0; i < strc.Count; i++) {
|
|
string str = ".long " + strc[i];
|
|
str = str.PadRight(40) + "| " + i + " - " + ((Op)i).ToString();
|
|
tw.WriteLine(str);
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// Clip tables
|
|
EmitLeftClipTables(tw);
|
|
EmitRightClipTables(tw);
|
|
|
|
tw.Close();
|
|
}
|
|
|
|
static OpType GetOpType(Op op) {
|
|
if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd)
|
|
return OpType.Data;
|
|
if (op >= Op.OddDataStart && op <= Op.OddDataEnd)
|
|
return OpType.Data;
|
|
if (op >= Op.EvenDataNStart && op <= Op.EvenDataNEnd)
|
|
return OpType.Data;
|
|
if (op >= Op.OddDataNStart && op <= Op.OddDataNEnd)
|
|
return OpType.Data;
|
|
if (op >= Op.SideStart && op <= Op.SideEnd)
|
|
return OpType.Side;
|
|
if (op == Op.SideN)
|
|
return OpType.Side;
|
|
if (op >= Op.ShadowStart && op <= Op.ShadowEnd)
|
|
return OpType.Shadow;
|
|
if (op == Op.ShadowN)
|
|
return OpType.Shadow;
|
|
if (op == Op.TransparentN)
|
|
return OpType.Transparent;
|
|
Debug.Assert(false);
|
|
return OpType.None;
|
|
}
|
|
|
|
static Align GetOpRightAlignment(Op op) {
|
|
// If it's not data, we don't know the alignment
|
|
if (GetOpType(op) != OpType.Data)
|
|
return Align.None;
|
|
|
|
if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd) {
|
|
int cp = (int)(op - (int)Op.EvenDataStart) / 2 + 1;
|
|
return (cp & 1) != 0 ? Align.Odd : Align.Even;
|
|
}
|
|
|
|
if (op >= Op.OddDataStart && op <= Op.OddDataEnd) {
|
|
int cp = (int)(op - (int)Op.OddDataStart) / 2 + 1;
|
|
return (cp & 1) != 0 ? Align.Even : Align.Odd;
|
|
}
|
|
|
|
switch (op) {
|
|
case Op.EvenDataLB:
|
|
case Op.EvenDataLB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.EvenDataLW:
|
|
case Op.EvenDataLW_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.EvenDataLWB:
|
|
case Op.EvenDataLWB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.EvenDataL:
|
|
case Op.EvenDataL_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.OddDataLB:
|
|
case Op.OddDataLB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.OddDataLW:
|
|
case Op.OddDataLW_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.OddDataLWB:
|
|
case Op.OddDataLWB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.OddDataL:
|
|
case Op.OddDataL_Inc:
|
|
return Align.Even;
|
|
}
|
|
|
|
Debug.Assert(false);
|
|
return Align.None;
|
|
}
|
|
|
|
static Align GetOpLeftAlignment(Op op) {
|
|
// If it's not data, we don't know the alignment
|
|
if (GetOpType(op) != OpType.Data)
|
|
return Align.None;
|
|
|
|
if (op >= Op.EvenDataStart && op <= Op.EvenDataEnd)
|
|
return Align.Even;
|
|
|
|
if (op >= Op.OddDataStart && op <= Op.OddDataEnd)
|
|
return Align.Odd;
|
|
|
|
switch (op) {
|
|
case Op.EvenDataLB:
|
|
case Op.EvenDataLB_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.EvenDataLW:
|
|
case Op.EvenDataLW_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.EvenDataLWB:
|
|
case Op.EvenDataLWB_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.EvenDataL:
|
|
case Op.EvenDataL_Inc:
|
|
return Align.Even;
|
|
|
|
case Op.OddDataLB:
|
|
case Op.OddDataLB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.OddDataLW:
|
|
case Op.OddDataLW_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.OddDataLWB:
|
|
case Op.OddDataLWB_Inc:
|
|
return Align.Odd;
|
|
|
|
case Op.OddDataL:
|
|
case Op.OddDataL_Inc:
|
|
return Align.Odd;
|
|
}
|
|
|
|
Debug.Assert(false);
|
|
return Align.None;
|
|
}
|
|
|
|
static void EmitLeftClipTables(TextWriter tw) {
|
|
// Emit gmpopapfnLeftClip
|
|
tw.WriteLine(".globl gmpopapfnLeftClip");
|
|
tw.WriteLine("gmpopapfnLeftClip:");
|
|
for (Op op = Op.EvenData1; op < Op.EndScan; op++) {
|
|
string strT = null;
|
|
switch (GetOpType(op)) {
|
|
case OpType.Data:
|
|
if (GetOpRightAlignment(op) == Align.Even) {
|
|
strT = ".long gmpcpRightpfnREEData";
|
|
} else {
|
|
strT = ".long gmpcpRightpfnREOData";
|
|
}
|
|
break;
|
|
|
|
case OpType.Side:
|
|
strT = ".long gmpcppfnSide";
|
|
break;
|
|
|
|
case OpType.Shadow:
|
|
strT = ".long gmpcppfnShadow";
|
|
break;
|
|
|
|
case OpType.Transparent:
|
|
strT = ".long gmpcppfnTransparent";
|
|
break;
|
|
|
|
default:
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
if (strT != null)
|
|
tw.WriteLine(strT.PadRight(40) + "| " + (int)op + " - " + op.ToString());
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcpRightpfnREEData (REE == Right Edge Even)
|
|
tw.WriteLine("gmpcpRightpfnREEData:");
|
|
for (int cpRight = 0; cpRight < 256; cpRight++) {
|
|
if (cpRight == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
Align alignLeftEdge = (cpRight & 1) != 0 ? Align.Odd : Align.Even;
|
|
switch (alignLeftEdge) {
|
|
case Align.Even:
|
|
if (cpRight <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long EvenData" + cpRight + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long EvenDataN_Clip");
|
|
}
|
|
break;
|
|
|
|
case Align.Odd:
|
|
if (cpRight <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long OddData" + cpRight + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long OddDataN_Clip");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcpRightpfnREOData (REO == Right Edge Odd)
|
|
tw.WriteLine("gmpcpRightpfnREOData:");
|
|
for (int cpRight = 0; cpRight < 256; cpRight++) {
|
|
if (cpRight == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
Align alignLeftEdge = (cpRight & 1) != 0 ? Align.Even : Align.Odd;
|
|
switch (alignLeftEdge) {
|
|
case Align.Even:
|
|
if (cpRight <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long EvenData" + cpRight + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long EvenDataN_Clip");
|
|
}
|
|
break;
|
|
|
|
case Align.Odd:
|
|
if (cpRight <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long OddData" + cpRight + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long OddDataN_Clip");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcppfnSide
|
|
tw.WriteLine("gmpcppfnSide:");
|
|
for (int cpRight = 0; cpRight < 256; cpRight++) {
|
|
if (cpRight == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
if (cpRight <= s_cpSideOpFixedMax) {
|
|
tw.WriteLine(".long Side" + cpRight + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long SideN_Clip");
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcppfnShadow
|
|
tw.WriteLine("gmpcppfnShadow:");
|
|
for (int cpRight = 0; cpRight < 256; cpRight++) {
|
|
if (cpRight == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
if (cpRight <= s_cpShadowOpFixedMax) {
|
|
tw.WriteLine(".long Shadow" + cpRight);
|
|
} else {
|
|
tw.WriteLine(".long ShadowN_Clip");
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
//gmpcppfnTransparent
|
|
tw.WriteLine("gmpcppfnTransparent:");
|
|
for (int cpRight = 0; cpRight < 256; cpRight++) {
|
|
if (cpRight == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
tw.WriteLine(".long TransparentN_Clip");
|
|
}
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitRightClipTables(TextWriter tw) {
|
|
// Emit gmpopapfnRightClip
|
|
tw.WriteLine(".globl gmpopapfnRightClip");
|
|
tw.WriteLine("gmpopapfnRightClip:");
|
|
for (Op op = Op.EvenData1; op < Op.EndScan; op++) {
|
|
string strT = null;
|
|
switch (GetOpType(op)) {
|
|
case OpType.Data:
|
|
if (GetOpLeftAlignment(op) == Align.Even) {
|
|
strT = ".long gmpcpLeftpfnLEEData";
|
|
} else {
|
|
strT = ".long gmpcpLeftpfnLEOData";
|
|
}
|
|
break;
|
|
|
|
case OpType.Side:
|
|
strT = ".long gmpcppfnSide";
|
|
break;
|
|
|
|
case OpType.Shadow:
|
|
strT = ".long gmpcppfnShadow";
|
|
break;
|
|
|
|
case OpType.Transparent:
|
|
strT = ".long gmpcppfnTransparent";
|
|
break;
|
|
|
|
default:
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
if (strT != null)
|
|
tw.WriteLine(strT.PadRight(40) + "| " + (int)op + " - " + op.ToString());
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcpLeftpfnLEEData (LEE == Left Edge Even)
|
|
tw.WriteLine("gmpcpLeftpfnLEEData:");
|
|
for (int cpLeft = 0; cpLeft < 256; cpLeft++) {
|
|
if (cpLeft == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
if (cpLeft <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long EvenData" + cpLeft + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long EvenDataN_Clip");
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
|
|
// gmpcpLeftpfnLEOData (LEO == Left Edge Odd)
|
|
tw.WriteLine("gmpcpLeftpfnLEOData:");
|
|
for (int cpLeft = 0; cpLeft < 256; cpLeft++) {
|
|
if (cpLeft == 0) {
|
|
tw.WriteLine(".long 0");
|
|
continue;
|
|
}
|
|
if (cpLeft <= s_cpDataOpFixedMax) {
|
|
tw.WriteLine(".long OddData" + cpLeft + "_Clip");
|
|
} else {
|
|
tw.WriteLine(".long OddDataN_Clip");
|
|
}
|
|
}
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitDataNHandler(TextWriter tw, string strLabel, Align align, int cp, StringCollection strc) {
|
|
strc.Add(strLabel);
|
|
strc.Add(strLabel + "_Inc");
|
|
tw.WriteLine(strLabel + "_Inc:");
|
|
tw.WriteLine("\taddq.w #1,%a0");
|
|
tw.WriteLine(strLabel + ":");
|
|
tw.WriteLine("\tmove.b (%a0)+,%d0");
|
|
if (align == Align.Odd) {
|
|
tw.WriteLine("\tmove.b (%a0)+,(%a1)+");
|
|
cp--;
|
|
align = Align.Even;
|
|
}
|
|
tw.WriteLine("\tjmp 2(%pc,%d0.w)");
|
|
EmitAlignedDataCopy(tw, cp, align);
|
|
tw.WriteLine("\tmove.l (%a2)+,%a3");
|
|
tw.WriteLine("\tjmp (%a3)");
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitShadowHandler(TextWriter tw, int cp, StringCollection strc) {
|
|
string strLabel = "Shadow" + cp;
|
|
strc.Add(strLabel);
|
|
tw.WriteLine(strLabel + ":");
|
|
while (cp-- != 0) {
|
|
tw.WriteLine("\tmove.b (%a1),%d0");
|
|
tw.WriteLine("\tmove.b (%a5,%d0.w),(%a1)+");
|
|
}
|
|
tw.WriteLine("\tmove.l (%a2)+,%a3");
|
|
tw.WriteLine("\tjmp (%a3)");
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitSideHandler(TextWriter tw, int cp, StringCollection strc) {
|
|
string strLabel = "Side" + cp;
|
|
strc.Add(strLabel);
|
|
|
|
tw.WriteLine(strLabel + "_Clip:");
|
|
tw.WriteLine("\tadd.w %d2,%a0");
|
|
tw.WriteLine(strLabel + ":");
|
|
while (cp-- != 0) {
|
|
tw.WriteLine("\tmove.b (%a0)+,%d0");
|
|
tw.WriteLine("\tmove.b (%a4,%d0.w),(%a1)+");
|
|
}
|
|
tw.WriteLine("\tmove.l (%a2)+,%a3");
|
|
tw.WriteLine("\tjmp (%a3)");
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitDataHandler(TextWriter tw, int cp, Align align, bool fClip, StringCollection strc) {
|
|
string strLabel = (align == Align.Even ? "EvenData" : "OddData") + cp;
|
|
if (!fClip) {
|
|
string strLabelInc = strLabel + "_Inc";
|
|
strc.Add(strLabel);
|
|
strc.Add(strLabelInc);
|
|
tw.WriteLine(strLabelInc + ":");
|
|
tw.WriteLine("\taddq.w #1,%a0");
|
|
tw.WriteLine(strLabel + ":");
|
|
} else {
|
|
tw.WriteLine(strLabel + "_Clip:");
|
|
tw.WriteLine("\tadd.w %d2,%a0");
|
|
}
|
|
EmitAlignedDataCopy(tw, cp, align);
|
|
tw.WriteLine("\tmove.l (%a2)+,%a3");
|
|
tw.WriteLine("\tjmp (%a3)");
|
|
tw.WriteLine("");
|
|
}
|
|
|
|
static void EmitAlignedDataCopy(TextWriter tw, int cp, Align align) {
|
|
Align alignWrite = align;
|
|
while (cp != 0) {
|
|
if (alignWrite == Align.Odd) {
|
|
tw.WriteLine("\tmove.b (%a0)+,(%a1)+");
|
|
cp--;
|
|
alignWrite = Align.Even;
|
|
continue;
|
|
}
|
|
switch (cp) {
|
|
case 1:
|
|
tw.WriteLine("\tmove.b (%a0)+,(%a1)+");
|
|
alignWrite = Align.Odd;
|
|
cp -= 1;
|
|
Debug.Assert(cp == 0);
|
|
break;
|
|
|
|
case 2:
|
|
tw.WriteLine("\tmove.w (%a0)+,(%a1)+");
|
|
cp -= 2;
|
|
break;
|
|
|
|
case 3:
|
|
tw.WriteLine("\tmove.w (%a0)+,(%a1)+");
|
|
tw.WriteLine("\tmove.b (%a0)+,(%a1)+");
|
|
alignWrite = Align.Odd;
|
|
cp -= 3;
|
|
Debug.Assert(cp == 0);
|
|
break;
|
|
|
|
case 4:
|
|
tw.WriteLine("\tmove.l (%a0)+,(%a1)+");
|
|
cp -= 4;
|
|
break;
|
|
|
|
default:
|
|
tw.WriteLine("\tmove.l (%a0)+,(%a1)+");
|
|
cp -= 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit code to draw the image. This is for data collection purposes at the moment
|
|
|
|
public void EmitCode(string str, Align align) {
|
|
TextWriter tw = Console.Out;
|
|
tw.WriteLine(".section code4,\"x\"");
|
|
tw.WriteLine(".even");
|
|
tw.WriteLine(".globl " + str);
|
|
tw.WriteLine(str + ":");
|
|
tw.WriteLine("\tlea " + str + "_data(%pc),%a0");
|
|
tw.WriteLine("\tmove.w #" + (m_cy - 1) + ",%d1");
|
|
|
|
Frame frame = (Frame)m_alsFrames[0];
|
|
ArrayList aalsRuns;
|
|
if (align == Align.Even) {
|
|
aalsRuns = frame.alsArunsEven;
|
|
} else {
|
|
aalsRuns = frame.alsArunsOdd;
|
|
}
|
|
|
|
int xEmitLast = 0;
|
|
int yEmitLast = 0;
|
|
ArrayList alsIclr = new ArrayList();
|
|
for (int y = 0; y < m_cy; y++) {
|
|
int x = 0;
|
|
Run[] arun = (Run[])aalsRuns[y];
|
|
foreach (Run run in arun) {
|
|
if (run.ct == ColorType.EndScan)
|
|
break;
|
|
Align alignDst;
|
|
if (align == Align.Even) {
|
|
alignDst = (x & 1) != 0 ? Align.Odd : Align.Even;
|
|
} else {
|
|
alignDst = (x & 1) == 0 ? Align.Odd : Align.Even;
|
|
}
|
|
Align alignSrc = (alsIclr.Count & 1) != 0 ? Align.Odd : Align.Even;
|
|
int cp;
|
|
|
|
// First destination stepping
|
|
switch (run.ct) {
|
|
case ColorType.Data:
|
|
case ColorType.Shadow:
|
|
case ColorType.Side:
|
|
if (y - yEmitLast > 1) {
|
|
int cyStep = y - yEmitLast;
|
|
if (cyStep <= 8) {
|
|
tw.WriteLine("\tsubq.w #" + cyStep + ",%d1");
|
|
} else {
|
|
tw.WriteLine("\tsub.w #" + cyStep + ",%d1");
|
|
}
|
|
tw.WriteLine("\tbpl.b 1f");
|
|
tw.WriteLine("\trts");
|
|
tw.WriteLine("1:");
|
|
int cbStep = ((y - yEmitLast) - 1) * 160 + (160 - xEmitLast) + x;
|
|
tw.WriteLine("\tadd.w #" + cbStep + ",%a1");
|
|
} else if (y - yEmitLast == 1) {
|
|
tw.WriteLine("\tdbra %d1,1f");
|
|
tw.WriteLine("\trts");
|
|
tw.WriteLine("1:");
|
|
int cbStep = ((y - yEmitLast) - 1) * 160 + (160 - xEmitLast) + x;
|
|
tw.WriteLine("\tadd.w #" + cbStep + ",%a1");
|
|
} else if (y == yEmitLast) {
|
|
int cbStep = x - xEmitLast;
|
|
if (cbStep <= 8) {
|
|
if (cbStep > 0)
|
|
tw.WriteLine("\taddq.w #" + cbStep + ",%a1");
|
|
} else {
|
|
tw.WriteLine("\tadd.w #" + cbStep + ",%a1");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Now handle run handling code
|
|
switch (run.ct) {
|
|
case ColorType.Data:
|
|
if (alignSrc != alignDst) {
|
|
alsIclr.Add(s_iclrNotAssigned);
|
|
tw.WriteLine("\taddq.w #1,%a0");
|
|
alignSrc = alignDst;
|
|
}
|
|
alsIclr.AddRange(run.aiclr);
|
|
EmitAlignedDataCopy(tw, run.cp, alignDst);
|
|
x += run.cp;
|
|
xEmitLast = x;
|
|
yEmitLast = y;
|
|
break;
|
|
|
|
case ColorType.Shadow:
|
|
cp = run.cp;
|
|
while (cp-- != 0) {
|
|
tw.WriteLine("\tmove.b (%a1),%d0");
|
|
tw.WriteLine("\tmove.b 0.b(%a5,%d0.w),(%a1)+");
|
|
}
|
|
x += run.cp;
|
|
xEmitLast = x;
|
|
yEmitLast = y;
|
|
break;
|
|
|
|
case ColorType.Side:
|
|
alsIclr.AddRange(run.aiclr);
|
|
cp = run.cp;
|
|
while (cp-- != 0) {
|
|
tw.WriteLine("\tmove.b (%a0)+,%d0");
|
|
tw.WriteLine("\tmove.b 0.b(%a4,%d0.w),(%a1)+");
|
|
}
|
|
x += run.cp;
|
|
xEmitLast = x;
|
|
yEmitLast = y;
|
|
break;
|
|
|
|
case ColorType.Transparent:
|
|
x += run.cp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
tw.WriteLine("\trts");
|
|
tw.WriteLine("");
|
|
|
|
// Emit data:
|
|
tw.WriteLine(".even");
|
|
tw.WriteLine(str + "_data:");
|
|
foreach (int iclr in alsIclr)
|
|
tw.WriteLine(".byte " + iclr);
|
|
tw.Close();
|
|
}
|
|
|
|
public int FrameCount {
|
|
get {
|
|
return m_alsFrames.Count;
|
|
}
|
|
}
|
|
}
|
|
}
|