public static byte[] Decompress(byte[] input, SnmVideoFrame video, Blocky16Context context) { if (input == null) { throw new ArgumentNullException(nameof(input)); } if (video == null) { throw new ArgumentNullException(nameof(video)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (video.Sequence == 0) { context.Clear(video.BackgroundColor); } switch (video.SubcodecId) { case 0: Array.Copy(input, 0, context.Buffer0, 0, context.BufferSize); break; case 1: throw new NotImplementedException("SubcodecId 1"); case 2: { int hblocks = (int)((video.Height + 7) & 0xFFFFFFF8); int wblocks = (int)((video.Width + 7) & 0xFFFFFFF8); int inputPos = 0; for (int cy = 0; cy < hblocks; cy += 8) { for (int cx = 0; cx < wblocks; cx += 8) { Level(8, input, ref inputPos, video, context, cx, cy); } } break; } case 3: Array.Copy(context.Buffer2, 0, context.Buffer0, 0, context.BufferSize); break; case 4: Array.Copy(context.Buffer1, 0, context.Buffer0, 0, context.BufferSize); break; case 5: for (int pos = 0, inputPos = 0; pos < video.RleOutputSize;) { byte code = input[inputPos]; inputPos++; int length = (code >> 1) + 1; if ((code & 1) != 0) { byte color = input[inputPos]; inputPos++; for (int i = 0; i < length; i++) { context.Buffer0[pos + i] = color; } } else { Array.Copy(input, inputPos, context.Buffer0, pos, length); inputPos += length; } pos += length; } break; case 6: for (int pos = 0, inputPos = 0; pos < context.BufferSize; pos += 2) { byte index = input[inputPos]; inputPos++; ushort color = video.Codebook[index]; context.Buffer0[pos] = (byte)color; context.Buffer0[pos + 1] = (byte)(color >> 8); } break; case 7: throw new NotImplementedException("SubcodecId 7"); case 8: for (int pos = 0, inputPos = 0; pos < video.RleOutputSize;) { byte code = input[inputPos]; inputPos++; int length2 = ((code >> 1) + 1) * 2; if ((code & 1) != 0) { byte index = input[inputPos]; inputPos++; ushort color = video.Codebook[index]; for (int i = 0; i < length2; i += 2) { context.Buffer0[pos + i] = (byte)color; context.Buffer0[pos + i + 1] = (byte)(color >> 8); } } else { for (int i = 0; i < length2; i += 2) { byte index = input[inputPos]; inputPos++; ushort color = video.Codebook[index]; context.Buffer0[pos + i] = (byte)color; context.Buffer0[pos + i + 1] = (byte)(color >> 8); } } pos += length2; } break; default: throw new NotSupportedException(); } byte[] output = context.Buffer0; context.RotateBuffers(video.DiffBufferRotateCode); return(output); }
private static void Level(int param, byte[] input, ref int inputPos, SnmVideoFrame video, Blocky16Context context, int cx, int cy) { byte opcode = input[inputPos]; inputPos++; int stride = video.Width * 2; int pos = cy * stride + cx * 2; if (opcode <= 0xF4) { int x = _motion_vectors[opcode * 2]; int y = _motion_vectors[opcode * 2 + 1]; int offset = y * stride + x * 2; for (int i = 0; i < param; i++) { if (pos + offset + param * 2 > context.BufferSize) { break; } Array.Copy(context.Buffer2, pos + offset, context.Buffer0, pos, param * 2); pos += stride; } } else if (opcode == 0xF5) { int motion_vector = BitConverter.ToInt16(input, inputPos); inputPos += 2; int x = motion_vector % video.Width; int y = motion_vector / video.Width; int offset = y * stride + x * 2; for (int i = 0; i < param; i++) { if (pos + offset + param * 2 > context.BufferSize) { break; } Array.Copy(context.Buffer2, pos + offset, context.Buffer0, pos, param * 2); pos += stride; } } else if (opcode == 0xF6) { for (int i = 0; i < param; i++) { if (pos + param * 2 > context.BufferSize) { break; } Array.Copy(context.Buffer1, pos, context.Buffer0, pos, param * 2); pos += stride; } } else if (opcode == 0xF7) { if (param > 2) { byte glyph_index = input[inputPos++]; byte fg_index = input[inputPos++]; byte bg_index = input[inputPos++]; ushort fg_color = video.Codebook[fg_index]; ushort bg_color = video.Codebook[bg_index]; byte[] glyph; int glyphWidth; if (param == 8) { glyph = _glyph8_cb[glyph_index]; glyphWidth = 8; } else { glyph = _glyph4_cb[glyph_index]; glyphWidth = 4; } byte bgLow = (byte)bg_color; byte bgHigh = (byte)(bg_color >> 8); byte fgLow = (byte)fg_color; byte fgHigh = (byte)(fg_color >> 8); for (int i = 0; i < param; i++) { for (int j = 0; j < param; j++) { if (glyph[i * glyphWidth + j] == 0) { context.Buffer0[pos + j * 2] = bgLow; context.Buffer0[pos + j * 2 + 1] = bgHigh; } else { context.Buffer0[pos + j * 2] = fgLow; context.Buffer0[pos + j * 2 + 1] = fgHigh; } } pos += stride; } } else { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2 * 2; j += 2) { byte index = input[inputPos++]; ushort color = video.Codebook[index]; context.Buffer0[pos + j] = (byte)color; context.Buffer0[pos + j + 1] = (byte)(color >> 8); } pos += stride; } } } else if (opcode == 0xF8) { if (param > 2) { byte glyph_index = input[inputPos++]; ushort fg_color = BitConverter.ToUInt16(input, inputPos); inputPos += 2; ushort bg_color = BitConverter.ToUInt16(input, inputPos); inputPos += 2; byte[] glyph; int glyphWidth; if (param == 8) { glyph = _glyph8_cb[glyph_index]; glyphWidth = 8; } else { glyph = _glyph4_cb[glyph_index]; glyphWidth = 4; } byte bgLow = (byte)bg_color; byte bgHigh = (byte)(bg_color >> 8); byte fgLow = (byte)fg_color; byte fgHigh = (byte)(fg_color >> 8); for (int i = 0; i < param; i++) { for (int j = 0; j < param; j++) { if (glyph[i * glyphWidth + j] == 0) { context.Buffer0[pos + j * 2] = bgLow; context.Buffer0[pos + j * 2 + 1] = bgHigh; } else { context.Buffer0[pos + j * 2] = fgLow; context.Buffer0[pos + j * 2 + 1] = fgHigh; } } pos += stride; } } else { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2 * 2; j += 2) { context.Buffer0[pos + j] = input[inputPos++]; context.Buffer0[pos + j + 1] = input[inputPos++]; } pos += stride; } } } else if (opcode >= 0xF9 && opcode <= 0xFC) { ushort color = video.SmallCodebook[opcode - 0xF9]; byte lowColor = (byte)color; byte highColor = (byte)(color >> 8); for (int i = 0; i < param; i++) { for (int j = 0; j < param * 2; j += 2) { context.Buffer0[pos + j] = lowColor; context.Buffer0[pos + j + 1] = highColor; } pos += stride; } } else if (opcode == 0xFD) { byte index = input[inputPos++]; ushort color = video.Codebook[index]; byte lowColor = (byte)color; byte highColor = (byte)(color >> 8); for (int i = 0; i < param; i++) { for (int j = 0; j < param * 2; j += 2) { context.Buffer0[pos + j] = lowColor; context.Buffer0[pos + j + 1] = highColor; } pos += stride; } } else if (opcode == 0xFE) { ushort color = BitConverter.ToUInt16(input, inputPos); inputPos += 2; byte lowColor = (byte)color; byte highColor = (byte)(color >> 8); for (int i = 0; i < param; i++) { for (int j = 0; j < param * 2; j += 2) { context.Buffer0[pos + j] = lowColor; context.Buffer0[pos + j + 1] = highColor; } pos += stride; } } else if (opcode == 0xFF) { if (param > 2) { int nextLevel = param / 2; Level(nextLevel, input, ref inputPos, video, context, cx, cy); Level(nextLevel, input, ref inputPos, video, context, cx + nextLevel, cy); Level(nextLevel, input, ref inputPos, video, context, cx, cy + nextLevel); Level(nextLevel, input, ref inputPos, video, context, cx + nextLevel, cy + nextLevel); } else { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2 * 2; j += 2) { context.Buffer0[pos + j] = input[inputPos++]; context.Buffer0[pos + j + 1] = input[inputPos++]; } pos += stride; } } } }