/// <summary> /// Writes the bitmap buffer to <paramref name="pngFile"/> and optional performs expansion if /// <paramref name="expand"/> is true. /// </summary> /// <param name="buffer">The buffer to the image bits.</param> /// <param name="std">The HG3STDINFO containing image dimensions, etc.</param> /// <param name="expand">True if the image should be expanded to its full size.</param> /// <param name="pngFile">The path to the file to save to.</param> private static void WritePng(byte[] buffer, HG2IMG std, HgxOptions options, string pngFile) { GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { IntPtr scan0 = handle.AddrOfPinnedObject(); int depthBytes = (std.DepthBits + 7) / 8; int stride = (std.Width * depthBytes + 3) & ~3; PixelFormat format, expandFormat = PixelFormat.Format32bppArgb; switch (std.DepthBits) { case 24: format = PixelFormat.Format24bppRgb; break; case 32: format = PixelFormat.Format32bppArgb; break; default: throw new HgxException($"Unsupported depth bits {std.DepthBits}!"); } // Do expansion here, and up to 32 bits if not 32 bits already. bool expand = options.HasFlag(HgxOptions.Expand); if (expand && (std.Width != std.TotalWidth || std.Height != std.TotalHeight)) { using (var bitmap = new Bitmap(std.Width, std.Height, stride, format, scan0)) using (var bitmapExpand = new Bitmap(std.TotalWidth, std.TotalHeight, expandFormat)) using (Graphics g = Graphics.FromImage(bitmapExpand)) { if (options.HasFlag(HgxOptions.Flip)) { bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); } g.DrawImageUnscaled(bitmap, std.OffsetX, std.OffsetY); bitmapExpand.Save(pngFile, ImageFormat.Png); } } else { using (var bitmap = new Bitmap(std.Width, std.Height, stride, format, scan0)) { if (options.HasFlag(HgxOptions.Flip)) { bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); } bitmap.Save(pngFile, ImageFormat.Png); } } } finally { // Thing to note that gave me headaches earlier: // Once this handle is freed, the bitmap loaded from // scan0 will be invalidated after garbage collection. handle.Free(); } }
/// <summary> /// Extracts the <see cref="HG2IMG"/> from the HG-2 file. /// </summary> /// <param name="reader">The binary reader for the file.</param> /// <param name="std">The HG3STDINFO containing image dimensions, etc.</param> /// <param name="img">The image header used to process the image.</param> /// <param name="expand">True if the image should be expanded to its full size.</param> /// <param name="pngFile">The path to the PNG file to save to.</param> private static void ExtractHg2Image(BinaryReader reader, Hg2FrameInfo frameInfo, HgxOptions options, string pngFile) { HG2IMG img = frameInfo.Img; reader.BaseStream.Position = frameInfo.Offset; int depthBytes = (img.DepthBits + 7) / 8; int stride = (img.Width * depthBytes + 3) & ~3; byte[] pixelBuffer = ProcessImage(reader, img.Width, img.Height, img.DepthBits, img.Data); if (!CatDebug.SpeedTestHgx) { // This image type is normally flipped, so reverse the option options ^= HgxOptions.Flip; WritePng(pixelBuffer, img, options, pngFile); } }
private static Hg2FrameInfo ReadHg2FrameInfo(BinaryReader reader, HGXHDR hdr, bool frameOnly) { Stream stream = reader.BaseStream; long frameOffset = reader.BaseStream.Position; HG2IMG img = reader.ReadUnmanaged <HG2IMG>(); HG2IMG_BASE?imgBase = null; if (hdr.Type == 0x25) { if (frameOnly) { stream.Position += HG2IMG_BASE.CbSize; } else { imgBase = reader.ReadUnmanaged <HG2IMG_BASE>(); } } return(new Hg2FrameInfo(reader.BaseStream, img, imgBase, frameOffset)); }