/// <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 WriteJpegToPng(byte[] buffer, HG3STDINFO std, HgxOptions options, string pngFile) { int depthBytes = (std.DepthBits + 7) / 8; int stride = (std.Width * depthBytes + 3) & ~3; PixelFormat expandFormat = PixelFormat.Format32bppArgb; // 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 ms = new MemoryStream(buffer)) using (var jpeg = (Bitmap)Image.FromStream(ms)) using (var bitmapExpand = new Bitmap(std.TotalWidth, std.TotalHeight, expandFormat)) using (Graphics g = Graphics.FromImage(bitmapExpand)) { if (options.HasFlag(HgxOptions.Flip)) { jpeg.RotateFlip(RotateFlipType.RotateNoneFlipY); } g.DrawImageUnscaled(jpeg, std.OffsetX, std.OffsetY); bitmapExpand.Save(pngFile, ImageFormat.Png); } } else { using (var ms = new MemoryStream(buffer)) using (var jpeg = (Bitmap)Image.FromStream(ms)) { if (options.HasFlag(HgxOptions.Flip)) { jpeg.RotateFlip(RotateFlipType.RotateNoneFlipY); } jpeg.Save(pngFile, ImageFormat.Png); } } }
/// <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> /// 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 unsafe void WriteJpegAlphaMaskToPng(byte[] buffer, byte[] alpha, HG3STDINFO std, HgxOptions options, string pngFile) { bool expand = options.HasFlag(HgxOptions.Expand); expand = expand && (std.Width != std.TotalWidth || std.Height != std.TotalHeight); int offsetX = (expand ? std.OffsetX : 0); int offsetY = (expand ? std.OffsetY : 0); int width = (expand ? std.TotalWidth : std.Width); int height = (expand ? std.TotalHeight : std.Height); int depthBytes = 4; int jpgStride = std.Width * depthBytes; int stride = width * depthBytes; int bufferSize = height * stride; int alphaStride = std.Width; BitmapData jpgData = null, bmpData = null; using (var ms = new MemoryStream(buffer)) using (var jpeg = (Bitmap)Image.FromStream(ms)) using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb)) { try { Rectangle rect = new Rectangle(0, 0, std.Width, std.Height); jpgData = jpeg.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); bmpData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); byte *pJpg = (byte *)jpgData.Scan0.ToPointer(); byte *pBmp = (byte *)bmpData.Scan0.ToPointer(); // Copy over the jpeg pixels first if (expand) { for (int y = 0; y < std.Height; y++) { int src = y * jpgStride; int dst = (y + offsetY) * stride + offsetX * depthBytes; Buffer.MemoryCopy(pJpg + src, pBmp + dst, bufferSize, jpgStride); } } else { Buffer.MemoryCopy(pJpg, pBmp, bufferSize, bufferSize); } // Now apply the alpha to the pixels for (int y = 0; y < std.Height; y++) { int src = y * alphaStride; int dst = (y + offsetY) * stride; for (int x = 0; x < std.Width; x++) { int alphaIndex = src + x; int pixelIndex = dst + (x + offsetX) * depthBytes; pBmp[pixelIndex + 3] = alpha[alphaIndex]; } } } finally { if (jpgData != null) { jpeg.UnlockBits(jpgData); } if (bmpData != null) { bitmap.UnlockBits(bmpData); } } if (options.HasFlag(HgxOptions.Flip)) { bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); } bitmap.Save(pngFile, ImageFormat.Png); } }