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; } } }