public static bool Unpack(string inputFile, string outputFile, ProcessData processData) { var fileStream = OpenFileOrDie(inputFile, FileMode.Open); try { ReadToStructure(fileStream, out AGFHDR hdr, Marshal.SizeOf <AGFHDR>()); if (hdr.type != AgfType24Bit && hdr.type != AgfType32Bit) { Program.Print(Program.ErrorColor, $"File {inputFile} was of unsupported type (possibly an MPEG): {hdr.type}"); return(false); } var bmpHeaderBuff = ReadSector(fileStream); var buff = ReadSector(fileStream); // Notice there's a gap of 2 bytes between these... alignment I guess. var bmf = ByteArrayToStructure <BITMAPFILEHEADER>(bmpHeaderBuff); var bmi = ByteArrayToStructure <BITMAPINFOHEADER>(bmpHeaderBuff, 16); int offset = 16 + Marshal.SizeOf <BITMAPINFOHEADER>(); RGBQUAD[] pal = ByteArrayToStructureArray <RGBQUAD>(bmpHeaderBuff, offset, bmpHeaderBuff.Length - offset); if (hdr.type == AgfType32Bit) { ReadToStructure(fileStream, out ACIFHDR acifHeader, Marshal.SizeOf <ACIFHDR>()); var alphaBuff = ReadSector(fileStream); var colorMap = DecodeColorMapWithAlpha(bmi, buff, pal, alphaBuff); processData.Decoding = new DecodingData(bmf, bmi, buff, pal, alphaBuff, colorMap, hdr, acifHeader); if (outputFile != null) { WriteBitmap(outputFile, colorMap, bmi.biWidth, bmi.biHeight, 4, null, true); } } else { processData.Decoding = new DecodingData(bmf, bmi, buff, pal, null, buff, hdr, default); if (outputFile != null) { WriteBitmap(outputFile, buff, bmi.biWidth, bmi.biHeight, bmi.biBitCount / 8, pal, bmf.bfOffBits == 54); } } } finally { fileStream.Dispose(); } return(true); }
public static bool Pack(string inputFileName, string outputFileName, ProcessData processData) { ReadBitmap(inputFileName, out var decodedData, out _, out var bmf, out var bmi); byte[] encodedData; byte[] outAlphaBuff = null; RGBQUAD[] palBuff; if (processData.Decoding.AgfHeader.type == AgfType32Bit) { EncodeColorMapWithAlpha(decodedData, bmi, processData, out encodedData, out outAlphaBuff, out palBuff); } else { palBuff = bmi.biBitCount == 8 ? ByteArrayToStructureArray <RGBQUAD>(decodedData, 0, 1024) : null; var decodedColorMap = bmi.biBitCount == 8 ? decodedData.Skip(1024) : decodedData; encodedData = decodedColorMap.ToArray(); } processData.Encoding = new DecodingData(bmf, bmi, encodedData, palBuff, outAlphaBuff, decodedData); WriteAgf(outputFileName, encodedData, palBuff, outAlphaBuff, processData); return(true); }
private static void WriteAgf(string fileName, byte[] encodedData, RGBQUAD[] palArray, byte[] alphaBuff, ProcessData processData) { var fileStream = OpenFileOrDie(fileName, FileMode.Create); try { fileStream.Write(StructToBytes(processData.Decoding.AgfHeader), 0, Marshal.SizeOf <AGFHDR>()); var hdrBytes = StructToBytes(processData.Decoding.Bmf).Concat(new byte[] { 0, 0 }) .Concat(StructToBytes(processData.Decoding.Bmi)); if (palArray != null) { hdrBytes = hdrBytes.Concat(palArray.SelectMany(StructToBytes)); } WriteSector(fileStream, hdrBytes.ToArray()); WriteSector(fileStream, encodedData); if (alphaBuff == null) { return; } fileStream.Write(StructToBytes(processData.Decoding.AcifHeader), 0, Marshal.SizeOf <ACIFHDR>()); WriteSector(fileStream, alphaBuff); } finally { fileStream.Dispose(); } }
private static void EncodeColorMapWithAlpha(byte[] decodedData, BITMAPINFOHEADER bmi, ProcessData processData, out byte[] encodedData, out byte[] alphaData, out RGBQUAD[] pal) { //must be padded to 4 bytes uint rgbStride = (uint)((bmi.biWidth * processData.Decoding.Bmi.biBitCount / 8 + 3) & ~3); alphaData = new byte[processData.Decoding.Bmi.biBitCount == 8 ? bmi.biHeight * bmi.biWidth : processData.Decoding.AlphaBuff.Length]; encodedData = new byte[processData.Decoding.Bmi.biBitCount == 8 ? bmi.biHeight * rgbStride : processData.Decoding.EncodedData.Length]; var palList = useExistingPal ? processData.Decoding?.PalArray.ToList() : new List <RGBQUAD>(); var additionalPalMap = new Dictionary <RGBQUAD, int>(); for (long y = 0; y < bmi.biHeight; y++) { uint alphaLineIndex = (uint)((bmi.biHeight - y - 1) * bmi.biWidth); uint rgbaLineIndex = (uint)(y * bmi.biWidth * 4); long rgbLineIndex = y * rgbStride; for (long x = 0; x < bmi.biWidth; x++) { long blueIndex = rgbaLineIndex + x * 4 + 0; if (processData.Decoding.Bmi.biBitCount == 8) { var newPal = new RGBQUAD { rgbBlue = decodedData[blueIndex], rgbGreen = decodedData[blueIndex + 1], rgbRed = decodedData[blueIndex + 2] }; var palIndex = palList.IndexOf(newPal); if (palIndex == -1) { palIndex = useExistingPal ? FindNearestPal(newPal, palList, additionalPalMap) : GetPal(newPal, palList, additionalPalMap); } encodedData[y * rgbStride + x] = (byte)palIndex; } else { encodedData[rgbLineIndex + x * 3] = decodedData[blueIndex]; encodedData[rgbLineIndex + x * 3 + 1] = decodedData[blueIndex + 1]; encodedData[rgbLineIndex + x * 3 + 2] = decodedData[blueIndex + 2]; } alphaData[alphaLineIndex + x] = decodedData[blueIndex + 3]; } } if (palList.Count < 256) { palList.AddRange(Enumerable.Repeat(new RGBQUAD(), 256 - palList.Count)); } pal = palList.ToArray(); }