/// <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);
                    }
            }
        }
Пример #2
0
        private static byte[] ProcessImageInternal(BinaryReader reader, HG3STDINFO std, HG3IMG img,
                                                   out byte[] rgbaBuffer)
        {
            int depthBytes = (std.DepthBits + 7) / 8;

            byte[] bufferTmp    = reader.ReadBytes(img.DataLength);
            byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);

            rgbaBuffer = new byte[std.Height * std.Width * 4];
            ProcessImage(
                bufferTmp,
                img.DataLength,
                img.OriginalDataLength,
                cmdBufferTmp,
                img.CmdLength,
                img.OriginalCmdLength,
                out IntPtr pRgbaBuffer,
                out int rgbaLength,
                std.Width,
                std.Height,
                depthBytes);

            rgbaBuffer = new byte[rgbaLength];
            Marshal.Copy(pRgbaBuffer, rgbaBuffer, 0, rgbaLength);
            Marshal.FreeHGlobal(pRgbaBuffer);
            return(rgbaBuffer);
        }
        private static void ExtractHg3ImageJpegAlpha(BinaryReader reader, Hg3FrameInfo frameInfo, HgxOptions options,
                                                     string pngFile)
        {
            HG3STDINFO std = frameInfo.StdInfo;
            HG3IMG_AL  img = frameInfo.ImgAl.Data;
            HG3TAG     tag = frameInfo.ImgJpg.Tag;

            reader.BaseStream.Position = frameInfo.ImgAl.Offset;

            // WGC handles bad alpha buffer errors gracefully, so we should too
            byte[] alphaBuffer;
            try {
                alphaBuffer = Zlib.Decompress(reader, img.CompressedLength, img.DecompressedLength);
            } catch (ZlibException ex) {
                if (ex.Result == ZResult.DataError || ex.Result == ZResult.BufferError)
                {
                    alphaBuffer = ex.OutputBuffer;
                }
                else
                {
                    throw;
                }
            }

            reader.BaseStream.Position = frameInfo.ImgJpg.Offset;

            byte[] buffer = reader.ReadBytes(tag.Length);

            if (!CatDebug.SpeedTestHgx)
            {
                WriteJpegAlphaMaskToPng(buffer, alphaBuffer, std, options, pngFile);
            }
        }
        private static void ExtractHg3ImageAlpha(BinaryReader reader, Hg3FrameInfo frameInfo, HgxOptions options,
                                                 string pngFile)
        {
            HG3STDINFO std = frameInfo.StdInfo;
            HG3IMG_AL  img = frameInfo.ImgAl.Data;

            reader.BaseStream.Position = frameInfo.ImgAl.Offset;

            // WGC handles bad alpha buffer errors gracefully, so we should too
            byte[] alphaBuffer;
            try {
                alphaBuffer = Zlib.Decompress(reader, img.CompressedLength, img.DecompressedLength);
            } catch (ZlibException ex) {
                if (ex.Result == ZResult.DataError || ex.Result == ZResult.BufferError)
                {
                    alphaBuffer = ex.OutputBuffer;
                }
                else
                {
                    throw;
                }
            }

            int depthBytes  = (std.DepthBits + 7) / 8;
            int stride      = (std.Width * depthBytes + 3) & ~3;
            int minStride   = (std.Width * depthBytes);
            int alphaStride = std.Width;

            byte[] pixelBuffer = new byte[stride * std.Height];
            for (int y = 0; y < std.Height; y++)
            {
                int src = y * alphaStride;
                int dst = y * stride;
                for (int x = 0; x < std.Width; x++)
                {
                    int  alphaIndex = src + x;
                    int  pixelIndex = dst + x * depthBytes;
                    byte alpha      = unchecked ((byte)(byte.MaxValue - alphaBuffer[alphaIndex]));
                    pixelBuffer[pixelIndex + 0] = alpha;
                    pixelBuffer[pixelIndex + 1] = alpha;
                    pixelBuffer[pixelIndex + 2] = alpha;
                    if (depthBytes == 4)
                    {
                        pixelBuffer[pixelIndex + 3] = byte.MaxValue;
                    }
                }
            }

            if (!CatDebug.SpeedTestHgx)
            {
                WritePng(pixelBuffer, std, options, pngFile);
            }
        }
        /*private static byte[] CopyFlipBufferAndDispose(IntPtr pRgbaBuffer, int stride, int height) {
         *      try {
         *              // Vertically flip the buffer so its in the correct setup to load into Bitmap
         *              byte[] pixelBuffer = new byte[stride * height];
         *              for (int y = 0; y < height; y++) {
         *                      int src = y * stride;
         *                      int dst = (height - (y + 1)) * stride;
         *                      Marshal.Copy(pRgbaBuffer + src, pixelBuffer, dst, stride);
         *              }
         *              return pixelBuffer;
         *      } finally {
         *              Marshal.FreeHGlobal(pRgbaBuffer);
         *      }
         * }*/
        /*private static byte[] PrepareBufferAndDispose(IntPtr pRgbaBuffer, int stride, int height, bool flip) {
         *      try {
         *              byte[] pixelBuffer = new byte[stride * height];
         *              if (!flip) {
         *                      Marshal.Copy(pRgbaBuffer, pixelBuffer, 0, stride * height);
         *              }
         *              else {
         *                      // Vertically flip the buffer so its in the correct setup to load into Bitmap
         *                      for (int y = 0; y < height; y++) {
         *                              int src = y * stride;
         *                              int dst = (height - (y + 1)) * stride;
         *                              Marshal.Copy(pRgbaBuffer + src, pixelBuffer, dst, stride);
         *                      }
         *              }
         *              return pixelBuffer;
         *      } finally {
         *              Marshal.FreeHGlobal(pRgbaBuffer);
         *      }
         * }*/
        /*private static void FlipBuffer(byte[] rgbaBuffer, int width, int height, int depthBits, bool flip) {
         *      if (!flip)
         *              return;
         *      int depthBytes = (depthBits + 7) / 8;
         *      int stride = (width * depthBytes + 3) & ~3;
         *
         *      // Vertically flip the buffer so its in the correct setup to load into Bitmap
         *      byte[] strideBuffer = new byte[stride];
         *      // No issue if height is odd and we skip the middle line, we'd be swapping it with itself.
         *      for (int y = 0; y < height / 2; y++) {
         *              int src = y * stride;
         *              int dst = (height - (y + 1)) * stride;
         *              // Copy src to swap stride
         *              Buffer.BlockCopy(rgbaBuffer, src, strideBuffer, 0, stride);
         *              // Swap src with dst stride
         *              Buffer.BlockCopy(rgbaBuffer, dst, rgbaBuffer, src, stride);
         *              // Swap dst with swap stride
         *              Buffer.BlockCopy(strideBuffer, 0, rgbaBuffer, dst, stride);
         *      }
         * }*/

        #endregion

        #region ExtractBitmap

        /// <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, HG3STDINFO 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();
            }
        }
        private static void ExtractHg3ImageJpeg(BinaryReader reader, Hg3FrameInfo frameInfo, HgxOptions options,
                                                string pngFile)
        {
            HG3STDINFO std = frameInfo.StdInfo;
            HG3TAG     tag = frameInfo.ImgJpg.Tag;

            reader.BaseStream.Position = frameInfo.ImgJpg.Offset;

            byte[] buffer = reader.ReadBytes(tag.Length);

            if (!CatDebug.SpeedTestHgx)
            {
                WriteJpegToPng(buffer, std, options, pngFile);
            }
        }
        /// <summary>
        ///  Extracts the <see cref="HG3IMG"/> from the HG-3 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 ExtractHg3ImageStandard(BinaryReader reader, Hg3FrameInfo frameInfo, HgxOptions options,
                                                    string pngFile)
        {
            HG3STDINFO std = frameInfo.StdInfo;
            HG3IMG     img = frameInfo.Img.Data;

            reader.BaseStream.Position = frameInfo.Img.Offset;

            byte[] pixelBuffer = ProcessImage(reader, std.Width, std.Height, std.DepthBits, img.Data);

            if (!CatDebug.SpeedTestHgx)
            {
                // This image type is normally flipped, so reverse the option
                options ^= HgxOptions.Flip;
                WritePng(pixelBuffer, std, options, pngFile);
            }
        }
Пример #8
0
        /// <summary>
        ///  Extracts the bitmap from the HG-3 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 ExtractBitmap(BinaryReader reader, HG3STDINFO std, HG3IMG img, bool expand, string pngFile)
        {
            int depthBytes = (std.DepthBits + 7) / 8;
            int stride     = (std.Width * depthBytes + 3) / 4 * 4;

            byte[] bufferTmp    = reader.ReadBytes(img.DataLength);
            byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
            byte[] rgbaBuffer;

            // Perform heavy processing that's faster in native code
            ProcessImageNative(
                bufferTmp,
                img.DataLength,
                img.OriginalDataLength,
                cmdBufferTmp,
                img.CmdLength,
                img.OriginalCmdLength,
                out IntPtr pRgbaBuffer,
                out int rgbaLength,
                std.Width,
                std.Height,
                depthBytes);

            try {
                // Vertically flip the buffer so its in the correct setup to load into Bitmap
                rgbaBuffer = new byte[rgbaLength];
                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    int dst = (std.Height - (y + 1)) * stride;
                    Marshal.Copy(pRgbaBuffer + src, rgbaBuffer, dst, stride);
                }
            } finally {
                Marshal.FreeHGlobal(pRgbaBuffer);
            }

            WriteBitmap(rgbaBuffer, std, expand, pngFile);
        }
Пример #9
0
        // This encoding tries to optimize for lots of zeros. I think. :)

        /*private static byte UnpackVal(byte c) {
         *      byte z = (byte) ((c & 1) != 0 ? 0xFF : 0);
         *      return (byte) ((c >> 1) ^ z);
         * }
         *
         * private static unsafe void UndeltaFilter(byte[] buffer,
         *      //int length,
         *      byte[] outBuffer,
         *      int width,
         *      int height,
         *      int depthBytes)
         * {
         *      uint[] table1 = new uint[256];
         *      uint[] table2 = new uint[256];
         *      uint[] table3 = new uint[256];
         *      uint[] table4 = new uint[256];
         *
         *      for (uint i = 0; i < 256; i++) {
         *              uint val = i & 0xC0;
         *
         *              val <<= 6;
         *              val |= i & 0x30;
         *
         *              val <<= 6;
         *              val |= i & 0x0C;
         *
         *              val <<= 6;
         *              val |= i & 0x03;
         *
         *              table4[i] = val;
         *              table3[i] = val << 2;
         *              table2[i] = val << 4;
         *              table1[i] = val << 6;
         *      }
         *
         *      uint sectLength = (uint) buffer.Length / 4;
         *
         *      fixed (byte* pOutBuffer = outBuffer)
         *      fixed (byte* pBuffer = buffer) {
         *              byte* sect1 = pBuffer;
         *              byte* sect2 = sect1 + sectLength;
         *              byte* sect3 = sect2 + sectLength;
         *              byte* sect4 = sect3 + sectLength;
         *
         *              byte* outP = pOutBuffer;
         *              byte* outEnd = pOutBuffer + buffer.Length;
         *
         *              while (outP < outEnd) {
         *                      uint val = table1[*sect1++] | table2[*sect2++] | table3[*sect3++] | table4[*sect4++];
         *
         * outP++ = UnpackVal((byte) (val >> 0));
         * outP++ = UnpackVal((byte) (val >> 8));
         * outP++ = UnpackVal((byte) (val >> 16));
         * outP++ = UnpackVal((byte) (val >> 24));
         *              }
         *
         *              int stride = width * depthBytes;
         *
         *              for (int x = depthBytes; x < stride; x++) {
         *                      outBuffer[x] += outBuffer[x - depthBytes];
         *              }
         *
         *              for (uint y = 1; y < height; y++) {
         *                      byte* line = pOutBuffer + y * stride;
         *                      byte* prev = pOutBuffer + (y - 1) * stride;
         *
         *                      for (uint x = 0; x < stride; x++) {
         *                              line[x] += prev[x];
         *                      }
         *              }
         *      }
         * }
         *
         * private static void Unrle(byte[] buffer,
         *              byte[] cmdBuffer,
         *              out byte[] outBuffer)
         * {
         *      BitBuffer cmdBits = new BitBuffer(cmdBuffer);
         *
         *      bool copyFlag = cmdBits.GetBit();
         *
         *      int outLength = (int) cmdBits.GetEliasGammaValue();
         *      outBuffer = new byte[outLength];
         *
         *      int n = 0;
         *      int index = 0;
         *      for (int i = 0; i < outLength; i += n) {
         *              n = (int) cmdBits.GetEliasGammaValue();
         *
         *              if (copyFlag) {
         *                      Array.Copy(buffer, index, outBuffer, i, n);
         *                      index += n;
         *              }
         *              else {
         *                      Array.Clear(outBuffer, i, n);
         *              }
         *
         *              copyFlag = !copyFlag;
         *      }
         * }*/
        #endregion

        private static void ProcessImage(BinaryReader reader,
                                         string file, HG3STDINFO std, HG3IMG img, bool expand = true)
        {
            int depthBytes = (std.DepthBits + 7) / 8;
            int stride     = (std.Width * depthBytes + 3) / 4 * 4;

            #region Old Code

            /*byte[] buffer = new byte[img.OriginalDataLength];
             * {
             *      byte[] temp = reader.ReadBytes(img.DataLength);
             *      Uncompress(buffer, ref img.OriginalDataLength,
             *              temp, img.DataLength);
             * }
             *
             * byte[] cmdBuffer = new byte[img.OriginalCmdLength];
             * {
             *      byte[] temp = reader.ReadBytes(img.CmdLength);
             *      Uncompress(cmdBuffer, ref img.OriginalCmdLength,
             *              temp, img.CmdLength);
             * }
             *
             * Unrle(buffer, cmdBuffer, out byte[] outBuffer);//, ref outLength);
             *
             * byte[] rgbaBuffer = new byte[outBuffer.Length];
             * UndeltaFilter(outBuffer, rgbaBuffer, std.Width, std.Height, depthBytes);
             * UnrleDeltaFilter(
             *      buffer, buffer.Length,
             *      cmdBuffer, cmdBuffer.Length,
             *      out IntPtr pRgbaBuffer, out int rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);*/

            /*byte[] bufferTmp = reader.ReadBytes(img.DataLength);
             * byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
             *
             * ProcessImage(
             *      bufferTmp,
             *      img.DataLength,
             *      img.OriginalDataLength,
             *      cmdBufferTmp,
             *      img.CmdLength,
             *      img.OriginalCmdLength,
             *      out IntPtr pRgbaBuffer,
             *      out int rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);
             *
             * byte[] rgbaBuffer = new byte[rgbaLength];
             * Marshal.Copy(pRgbaBuffer, rgbaBuffer, 0, rgbaLength);
             * Marshal.FreeCoTaskMem(pRgbaBuffer);*/
            #endregion

            ProcessImageInternal(reader, std, img, out byte[] rgbaBuffer);
            //int depthBytes = std.DepthBits / 8;

            /*byte[] bufferTmp = reader.ReadBytes(img.DataLength);
             * byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
             *
             * int rgbaLength;
             * byte[] rgbaBuffer = new byte[std.Height * std.Width * 4];
             * ProcessImage(
             *      bufferTmp,
             *      img.DataLength,
             *      img.OriginalDataLength,
             *      cmdBufferTmp,
             *      img.CmdLength,
             *      img.OriginalCmdLength,
             *      rgbaBuffer,
             *      //out IntPtr pRgbaBuffer,
             *      out rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);
             * if (rgbaBuffer.Length != rgbaLength) {
             *      //Console.WriteLine();
             *      Console.WriteLine($"\n{rgbaLength - rgbaBuffer.Length}                ");
             *      Console.Beep(500, 2000);
             *      return;
             * }*/

            if (expand)
            {
                int offsetXBytes = std.OffsetX * depthBytes;
                //int offsetYVert = std.TotalHeight - std.OffsetY - std.Height;

                int    expStride = (std.TotalWidth * depthBytes + 3) / 4 * 4;
                byte[] expBuffer = new byte[std.TotalHeight * expStride];

                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    //int dst = (y + offsetYVert) * expStride + offsetXBytes;
                    int dst = (std.Height - (y + 1) + std.OffsetY) * expStride + offsetXBytes;
                    Array.Copy(rgbaBuffer, src, expBuffer, dst, stride);
                }

                std.Width  = std.TotalWidth;
                std.Height = std.TotalHeight;
                rgbaBuffer = expBuffer;
            }
            else
            {
                byte[] flipBuffer = new byte[rgbaBuffer.Length];
                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    int dst = (std.Height - (y + 1)) * stride;
                    Array.Copy(rgbaBuffer, src, flipBuffer, dst, stride);
                }
                rgbaBuffer = flipBuffer;
            }

            WritePng(file, rgbaBuffer, std.Width, std.Height, std.DepthBits);
        }
Пример #10
0
        public static string[] Run(Stream stream, string hg3File, string outputDir,
                                   string fileName, bool expand)
        {
            BinaryReader reader = new BinaryReader(stream);
            HG3HDR       hdr    = reader.ReadStruct <HG3HDR>();

            if (hdr.Signature != "HG-3")
            {
                throw new InvalidFileException(Path.GetFileName(hg3File), "HG3");
            }

            List <string> files     = new List <string>();
            int           backtrack = Marshal.SizeOf <HG3TAG>() - 1;

            for (int i = 0; true; i++)
            {
                HG3TAG tag = reader.ReadStruct <HG3TAG>();

                // NEW METHOD: Keep searching for the next stdinfo
                // This way we don't miss any images
                while (!tag.Signature.StartsWith("stdinfo"))
                {
                    if (stream.IsEndOfStream())
                    {
                        break;
                    }
                    stream.Position -= backtrack;
                    tag              = reader.ReadStruct <HG3TAG>();
                }
                if (stream.IsEndOfStream())
                {
                    break;
                }

                // OLD METHOD: Missed entries in a few files
                //if (!tag.signature.StartsWith(StdInfoSignature))
                //	break;

                HG3STDINFO stdInfo = reader.ReadStruct <HG3STDINFO>();

                int imgIndex = 0;

                while (tag.OffsetNext != 0)
                {
                    tag = reader.ReadStruct <HG3TAG>();

                    string pngFile = Path.Combine(outputDir, MakeFileName(
                                                      fileName, i > 0 || hdr.EntryCount > 0, i, imgIndex++));

                    /*if (tag.Signature.StartsWith("img_al")) {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     *      HG3IMGAL imghdr = reader.ReadStruct<HG3IMGAL>();
                     *
                     *      int length = imghdr.length;
                     *      byte[] buffer = reader.ReadBytes(length);
                     *
                     *      int outLength = imghdr.original_length;
                     *      byte[] outBuffer = reader.ReadBytes(outLength);
                     *
                     *      Uncompress(outBuffer, ref outLength, buffer, length);
                     *
                     *      files.Add(pngFile);
                     *      WritePng(pngFile,
                     *              outBuffer,
                     *              stdInfo.width,
                     *              stdInfo.height,
                     *              1,
                     *              true);
                     * }
                     * else if (tag.Signature.StartsWith("img_jpg")) {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (tag.Signature == "imgmode") {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else */if (Regex.IsMatch(tag.Signature, @"img\d+"))
                    {
                        HG3IMG imghdr = reader.ReadStruct <HG3IMG>();

                        files.Add(pngFile);
                        ProcessImage(reader,
                                     pngFile,
                                     stdInfo,
                                     imghdr,
                                     expand);

                        /*stdInfo.Width,
                         * stdInfo.Height,
                         * stdInfo.DepthBits / 8,
                         * stdInfo.TotalWidth,
                         * stdInfo.TotalHeight,
                         * stdInfo.OffsetX,
                         * stdInfo.OffsetY,
                         * imghdr.DataLength,
                         * imghdr.OriginalDataLength,
                         * imghdr.CmdLength,
                         * imghdr.OriginalCmdLength);*/
                    }
                    else
                    {
                        // Skip this tag
                        stream.Position += tag.Length;
                    }
                }

                stream.Position += 8;
            }

            return(files.ToArray());
        }
        private static Hg3FrameInfo ReadHg3FrameInfo(BinaryReader reader, HGXHDR hdr, bool frameOnly)
        {
            Stream stream      = reader.BaseStream;
            long   frameOffset = stream.Position;

            HG3FRAMEHDR frameHdr = reader.ReadUnmanaged <HG3FRAMEHDR>();

            long   tagStartPosition = stream.Position;
            HG3TAG tag = reader.ReadUnmanaged <HG3TAG>();

            if (!HG3STDINFO.HasTagSignature(tag.Signature))
            {
                throw new Exception("Expected \"stdinfo\" tag!");
            }

            HG3STDINFO   stdInfo   = reader.ReadUnmanaged <HG3STDINFO>();
            Hg3FrameInfo frameInfo = new Hg3FrameInfo(frameHdr, stdInfo, frameOffset);

            while (tag.OffsetNext != 0)
            {
                stream.Position  = tagStartPosition + tag.OffsetNext;
                tagStartPosition = stream.Position;
                tag = reader.ReadUnmanaged <HG3TAG>();

                string signature = tag.Signature;

                if (HG3IMG.HasTagSignature(signature, out int imdId))                   // "img####"
                {
                    HG3IMG img = reader.ReadUnmanaged <HG3IMG>();
                    frameInfo.AddTagImg(stream, tag, img, imdId);
                }
                else if (HG3IMG_AL.HasTagSignature(signature))                   // "img_al"
                {
                    HG3IMG_AL img = reader.ReadUnmanaged <HG3IMG_AL>();
                    frameInfo.AddTagImg(stream, tag, img, 0);
                }
                else if (HG3IMG_JPG.HasTagSignature(signature))                   // "img_jpg"
                // There is no image info, reading it would increment by one byte which we don't want
                {
                    frameInfo.AddTagImg(stream, tag, new HG3IMG_JPG(), 0);
                }
                else if (frameOnly)
                {
                    if (frameInfo.Type != Hg3ImageType.None)
                    {
                        return(frameInfo);
                    }
                }
                else if (HG3ATS.HasTagSignature(signature, out int atsId))                   // "ats####"
                {
                    HG3ATS ats = reader.ReadUnmanaged <HG3ATS>();
                    frameInfo.Ats.Add(atsId, ats);
                }
                else if (HG3CPTYPE.HasTagSignature(signature))                   // "cptype"
                {
                    HG3CPTYPE cpType = reader.ReadUnmanaged <HG3CPTYPE>();
                    frameInfo.CpType = cpType;
                }
                else if (HG3IMGMODE.HasTagSignature(signature))                   // "imgmode"
                {
                    HG3IMGMODE imgMode = reader.ReadUnmanaged <HG3IMGMODE>();
                    frameInfo.ImgMode = imgMode;
                }
                else
                {
                    Trace.WriteLine($"UNKNOWN TAG: \"{signature}\"");
                }
            }
            return(frameInfo);
        }
Пример #12
0
        private static Hg3 Extract(Stream stream, string fileName, string directory, bool saveFrames, bool expand)
        {
            BinaryReader reader = new BinaryReader(stream);
            HG3HDR       hdr    = reader.ReadUnmanaged <HG3HDR>();

            if (hdr.Signature != "HG-3")
            {
                throw new UnexpectedFileTypeException(fileName, "HG-3");
            }

            //int backtrack = Marshal.SizeOf<HG3TAG>() - 1;
            List <KeyValuePair <HG3STDINFO, List <long> > > imageOffsets = new List <KeyValuePair <HG3STDINFO, List <long> > >();

            for (int i = 0; ; i++)
            {
                // NEW-NEW METHOD: We now know the next offset ahead
                // of time from the HG3OFFSET we're going to read.
                // Usually skips 0 bytes, otherwise usually 1-7 bytes.
                long      startPosition = stream.Position;
                HG3OFFSET offset        = reader.ReadUnmanaged <HG3OFFSET>();

                HG3TAG tag = reader.ReadUnmanaged <HG3TAG>();
                if (!HG3STDINFO.HasTagSignature(tag.Signature))
                {
                    throw new Exception("Expected \"stdinfo\" tag!");
                }

                // NEW METHOD: Keep searching for the next stdinfo
                // This way we don't miss any images

                /*int offset = 0;
                 * while (!tag.Signature.StartsWith("stdinfo")) {
                 *      if (stream.IsEndOfStream())
                 *              break;
                 *      stream.Position -= backtrack;
                 *      tag = reader.ReadStruct<HG3TAG>();
                 *      offset++;
                 * }
                 * if (stream.IsEndOfStream())
                 *      break;*/

                // OLD METHOD: Missed entries in a few files
                //if (!tag.signature.StartsWith(StdInfoSignature))
                //	break;

                HG3STDINFO stdInfo = reader.ReadUnmanaged <HG3STDINFO>();

                List <long> frameOffsets = new List <long>();
                imageOffsets.Add(new KeyValuePair <HG3STDINFO, List <long> >(stdInfo, frameOffsets));

                while (tag.OffsetNext != 0)
                {
                    tag = reader.ReadUnmanaged <HG3TAG>();

                    string signature = tag.Signature;
                    if (HG3IMG.HasTagSignature(signature))                       // "img####"
                    {
                        frameOffsets.Add(stream.Position);
                        // Skip this tag
                        stream.Position += tag.Length;
                    }

                    /*else if (HG3ATS.HasTagSignature(signature)) { // "ats####"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3CPTYPE.HasTagSignature(signature)) { // "cptype"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMG_AL.HasTagSignature(signature)) { // "img_al"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMG_JPG.HasTagSignature(signature)) { // "img_jpg"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMGMODE.HasTagSignature(signature)) { // "imgmode"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }*/
                    else
                    {
                        // Skip this unknown tag
                        stream.Position += tag.Length;
                    }
                }

                if (offset.OffsetNext == 0)
                {
                    break;                     // End of stream
                }
                stream.Position = startPosition + offset.OffsetNext;
            }

            HG3STDINFO[] stdInfos        = imageOffsets.Select(p => p.Key).ToArray();
            long[][]     allFrameOffsets = imageOffsets.Select(p => p.Value.ToArray()).ToArray();
            Hg3          hg3             = new Hg3(Path.GetFileName(fileName), hdr, stdInfos, allFrameOffsets, saveFrames && expand);

            // Save any frames after we've located them all.
            // This way we truely know if something is an animation.
            if (saveFrames)
            {
                for (int imgIndex = 0; imgIndex < hg3.Count; imgIndex++)
                {
                    HG3STDINFO stdInfo  = stdInfos[imgIndex];
                    Hg3Image   hg3Image = hg3.Images[imgIndex];
                    for (int frmIndex = 0; frmIndex < hg3Image.FrameCount; frmIndex++)
                    {
                        stream.Position = hg3Image.FrameOffsets[frmIndex];
                        HG3IMG imghdr  = reader.ReadUnmanaged <HG3IMG>();
                        string pngFile = hg3.GetFrameFilePath(directory, imgIndex, frmIndex);
                        ExtractBitmap(reader, stdInfo, imghdr, expand, pngFile);
                    }
                }
            }

            return(hg3);
        }
        /// <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);
                    }
        }