public void DecodeFrame(BitStream bs)
{
_mMapTree.Reset();
_mClrTree.Reset();
_fullTree.Reset();
_typeTree.Reset();
// Height needs to be doubled if we have flags (Y-interlaced or Y-doubled)
uint doubleY = (uint)(_flags != 0 ? 2 : 1);
uint bw = (uint)(Width / 4);
uint bh = Height / doubleY / 4;
int stride = Width;
uint block = 0, blocks = bw * bh;
uint type, run, j, mode;
uint p1, p2, clr, map;
byte hi, lo;
uint i;
int bOut;
byte[] pixels = _surface.Pixels;
while (block < blocks)
{
type = _typeTree.GetCode(bs);
run = GetBlockRun((int)((type >> 2) & 0x3f));
switch (type & 3)
{
case SmkBlockMono:
while (run-- != 0 && block < blocks)
{
clr = _mClrTree.GetCode(bs);
map = _mMapTree.GetCode(bs);
bOut = (int)((block / bw) * (stride * 4 * doubleY) + (block % bw) * 4);
hi = (byte)(clr >> 8);
lo = (byte)(clr & 0xff);
for (i = 0; i < 4; i++)
{
for (j = 0; j < doubleY; j++)
{
pixels[bOut] = (map & 1) != 0 ? hi : lo;
pixels[bOut + 1] = (map & 2) != 0 ? hi : lo;
pixels[bOut + 2] = (map & 4) != 0 ? hi : lo;
pixels[bOut + 3] = (map & 8) != 0 ? hi : lo;
bOut += stride;
}
map >>= 4;
}
++block;
}
break;
case SmkBlockFull:
// Smacker v2 has one mode, Smacker v4 has three
if (_signature == ScummHelper.MakeTag('S', 'M', 'K', '2'))
{
mode = 0;
}
else
{
// 00 - mode 0
// 10 - mode 1
// 01 - mode 2
mode = 0;
if (bs.GetBit() != 0)
{
mode = 1;
}
else if (bs.GetBit() != 0)
{
mode = 2;
}
}
while (run-- != 0 && block < blocks)
{
bOut = (int)((block / bw) * (stride * 4 * doubleY) + (block % bw) * 4);
switch (mode)
{
case 0:
for (i = 0; i < 4; ++i)
{
p1 = _fullTree.GetCode(bs);
p2 = _fullTree.GetCode(bs);
for (j = 0; j < doubleY; ++j)
{
pixels[bOut + 2] = (byte)(p1 & 0xff);
pixels[bOut + 3] = (byte)(p1 >> 8);
pixels[bOut + 0] = (byte)(p2 & 0xff);
pixels[bOut + 1] = (byte)(p2 >> 8);
bOut += stride;
}
}
break;
case 1:
p1 = _fullTree.GetCode(bs);
pixels[bOut + 0] = pixels[bOut + 1] = (byte)(p1 & 0xFF);
pixels[bOut + 2] = pixels[bOut + 3] = (byte)(p1 >> 8);
bOut += stride;
pixels[bOut + 0] = pixels[bOut + 1] = (byte)(p1 & 0xFF);
pixels[bOut + 2] = pixels[bOut + 3] = (byte)(p1 >> 8);
bOut += stride;
p2 = _fullTree.GetCode(bs);
pixels[bOut + 0] = pixels[bOut + 1] = (byte)(p2 & 0xFF);
pixels[bOut + 2] = pixels[bOut + 3] = (byte)(p2 >> 8);
bOut += stride;
pixels[bOut + 0] = pixels[bOut + 1] = (byte)(p2 & 0xFF);
pixels[bOut + 2] = pixels[bOut + 3] = (byte)(p2 >> 8);
bOut += stride;
break;
case 2:
for (i = 0; i < 2; i++)
{
// We first get p2 and then p1
// Check ffmpeg thread "[PATCH] Smacker video decoder bug fix"
// http://article.gmane.org/gmane.comp.video.ffmpeg.devel/78768
p2 = _fullTree.GetCode(bs);
p1 = _fullTree.GetCode(bs);
for (j = 0; j < doubleY; ++j)
{
pixels[bOut + 0] = (byte)(p1 & 0xff);
pixels[bOut + 1] = (byte)(p1 >> 8);
pixels[bOut + 2] = (byte)(p2 & 0xff);
pixels[bOut + 3] = (byte)(p2 >> 8);
bOut += stride;
}
for (j = 0; j < doubleY; ++j)
{
pixels[bOut + 0] = (byte)(p1 & 0xff);
pixels[bOut + 1] = (byte)(p1 >> 8);
pixels[bOut + 2] = (byte)(p2 & 0xff);
pixels[bOut + 3] = (byte)(p2 >> 8);
bOut += stride;
}
}
break;
}
++block;
}
break;
case SmkBlockSkip:
while (run-- != 0 && block < blocks)
{
block++;
}
break;
case SmkBlockFill:
uint col;
mode = type >> 8;
while (run-- != 0 && block < blocks)
{
bOut = (int)((block / bw) * (stride * 4 * doubleY) + (block % bw) * 4);
col = mode * 0x01010101;
for (i = 0; i < 4 * doubleY; ++i)
{
pixels[bOut + 0] = pixels[bOut + 1] = pixels[bOut + 2] = pixels[bOut + 3] = (byte)col;
bOut += stride;
}
++block;
}
break;
}
}
}