2014-03-06 296 views
0

压缩位图在我发布此问题之前,我在Google上搜索了大量搜索内容。 我想在我的Win窗体应用程序中使用RLE [运行长度编码]压缩来压缩8bpp位图。
我发现的大部分结果都来自VC++代码,我可以使用GDI +执行压缩吗? GDI +是否提供任何这样的类或方法?
我在msdn上找到了一个链接,但它并没有多大帮助。使用运行长度编码[RLE]使用GDI +

此外,我尝试使用“System.Drawing.Imaging.Encoder”编写以下代码,但输出图像
具有相同的大小。

private void button1_Click(object sender, EventArgs e) 
{ 
    Bitmap myBitmap; 
    ImageCodecInfo myImageCodecInfo; 
    Encoder myEncoder; 
    EncoderParameter myEncoderParameter; 
    EncoderParameters myEncoderParameters; 

    // Create a Bitmap object based on a BMP file. 
    myBitmap = new Bitmap("D:\\8BppImage.bmp"); 

    // Get an ImageCodecInfo object that represents the bmp codec. 
    myImageCodecInfo = GetEncoderInfo("image/bmp"); 

    // Create an Encoder object based on the GUID 
    // for the Compression parameter category. 
    myEncoder = Encoder.Compression; 

    // Create an EncoderParameters object. 
    myEncoderParameters = new EncoderParameters(1); 

    myEncoderParameter = new EncoderParameter(
     myEncoder,(long)EncoderValue.CompressionRle); 

    myEncoderParameters.Param[0] = myEncoderParameter; 

    myBitmap.Save("D:\\EncoderImg.bmp", myImageCodecInfo, myEncoderParameters); 

} 

private static ImageCodecInfo GetEncoderInfo(String mimeType) 
{ 
    int j; 
    ImageCodecInfo[] encoders; 
    encoders = ImageCodecInfo.GetImageEncoders(); 
    for (j = 0; j < encoders.Length; ++j) 
    { 
     if (encoders[j].MimeType == mimeType) 
      return encoders[j]; 
    } 
    return null; 
} 

我错过了什么吗?谁能帮我这个 ?

+0

你会得到相同的大小,因为RLE不是BMP和以前不压缩。正如你在这里指出的那样http://blogs.msdn.com/b/oldnewthing/archive/2009/04/08/9537051.aspx它可以在C++中完成,也许你可以Pinvoke GetDIBits?另一方面,你发送的链接解释了格式,你可以自己制作。 – Aybe

+0

RLE压缩被广泛忽视,使用真正广泛使用的压缩算法没有意义。 PNG是一个更好的格式。 –

回答

1

我已经提交的答案,用在这个问题上RLE在C#压缩8bpp的图片:How do I compress an image with Run-Length Encoding using C#?

我相信你正在尝试什么行不通,因为EncoderValue.CompressionRle仅适用于.TIFF图像。

这里是用来执行压缩代码:

private enum Compression 
    { 
     // others not necessary for the 8bpp compression, but left for reference 
     //BI_RGB = 0x0000, 
     BI_RLE8 = 0x0001, 
     //BI_RLE4 = 0x0002, 
     //BI_BITFIELDS = 0x0003, 
     //BI_JPEG = 0x0004, 
     //BI_PNG = 0x0005, 
     //BI_CMYK = 0x000B, 
     //BI_CMYKRLE8 = 0x000C, 
     //BI_CMYKRLE4 = 0x000D 
    } 

    private enum BitCount 
    { 
     // others not necessary for the 8bpp compression, but left for reference 
     //Undefined = (ushort)0x0000, 
     //TwoColors = (ushort)0x0001, 
     //Max16Colors = (ushort)0x0004, 
     Max256Colors = (ushort)0x0008, 
     //Max32KBColors = (ushort)0x0010, 
     //Max16MBColors = (ushort)0x0018, 
     //Max16MBColors_Compressed = (ushort)0x0020 
    } 

    private struct RleCompressedBmpHeader 
    { 
     // Everything before the HeaderSize is technically not part of the header (it's not included in the HeaderSize calculation) 

     /// <summary> 
     /// Size of the .bmp file. 
     /// Always header size (40), plus palette size, plus image size, plus pre-header size (14); 
     /// </summary> 
     public uint Size; 

     /// <summary> 
     /// Offset to start of image data in bytes from the start of the file 
     /// </summary> 
     public uint Offset; 

     /// <summary> 
     /// Size of this header in bytes. (Always 40) 
     /// </summary> 
     public uint HeaderSize; // 4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 4 

     /// <summary> 
     /// Width of bitmap in pixels 
     /// </summary> 
     public int Width; 

     /// <summary> 
     /// Height of bitmap in pixels 
     /// </summary> 
     public int Height; 

     /// <summary> 
     /// Number of Planes (layers). Always 1. 
     /// </summary> 
     public ushort Planes; 
     /// <summary> 
     /// Number of bits that define each pixel and maximum number of colors 
     /// </summary> 
     public BitCount BitCount; 

     /// <summary> 
     /// Defines the compression mode of the bitmap. 
     /// </summary> 
     public Compression Compression; 

     /// <summary> 
     /// Size, in bytes, of image. 
     /// </summary> 
     public uint ImageSize; 

     // These shouldn't really be all that important 
     public uint XPixelsPerMeter; 
     public uint YPixelsPerMeter; 

     /// <summary> 
     /// The number of indexes in the color table used by this bitmap. 
     /// <para>0 - Use max available</para> 
     /// <para>If BitCount is less than 16, this is the number of colors used by the bitmap</para> 
     /// <para>If BitCount is 16 or greater, this specifies the size of the color table used to optimize performance of the system palette.</para> 
     /// </summary> 
     public uint ColorUsed; 

     /// <summary> 
     /// Number of color indexes that are required for displaying the bitmap. 0 means all color indexes are required. 
     /// </summary> 
     public uint ColorImportant; 

     public byte[] ToBytes() 
     { 
      var swap = BitConverter.IsLittleEndian; 
      var result = new List<byte>(); 

      result.AddRange(new byte[] { 0x42, 0x4d }); // signature (BM) 
      result.AddRange(BitConverter.GetBytes(Size)); 
      result.AddRange(new byte[4]); // reserved 
      result.AddRange(BitConverter.GetBytes(Offset)); 
      result.AddRange(BitConverter.GetBytes(HeaderSize)); 
      result.AddRange(BitConverter.GetBytes(Width)); 
      result.AddRange(BitConverter.GetBytes(Height)); 
      result.AddRange(BitConverter.GetBytes(Planes)); 
      result.AddRange(BitConverter.GetBytes((ushort)BitCount)); 
      result.AddRange(BitConverter.GetBytes((uint)Compression)); 
      result.AddRange(BitConverter.GetBytes(ImageSize)); 
      result.AddRange(BitConverter.GetBytes(XPixelsPerMeter)); 
      result.AddRange(BitConverter.GetBytes(YPixelsPerMeter)); 
      result.AddRange(BitConverter.GetBytes(ColorUsed)); 
      result.AddRange(BitConverter.GetBytes(ColorImportant)); 

      return result.ToArray(); 
     } 
    } 

    public unsafe byte[] RunLengthEncodeBitmap(Bitmap bmp) 
    { 
     if (bmp.PixelFormat != PixelFormat.Format8bppIndexed) { throw new ArgumentException("The image must be in 8bppIndexed PixelFormat", "bmp"); } 

     var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); 
     List<byte> result = new List<byte>(); 

     // Actual RLE algorithm. Bottom of image is first stored row, so start from bottom. 
     for (var rowIndex = bmp.Height - 1; rowIndex >= 0; rowIndex--) 
     { 
      byte? storedPixel = null; 
      var curPixelRepititions = 0; 
      var imageRow = (byte*)data.Scan0.ToPointer() + (rowIndex * data.Stride); 
      for (var pixelIndex = 0; pixelIndex < bmp.Width; pixelIndex++) 
      { 
       var curPixel = imageRow[pixelIndex]; 
       if (!storedPixel.HasValue) 
       { 
        curPixelRepititions = 1; 
        storedPixel = curPixel; 
       } 
       else if (storedPixel.Value != curPixel || curPixelRepititions == 255) 
       { 
        result.Add(Convert.ToByte(curPixelRepititions)); 
        result.Add(storedPixel.Value); 
        curPixelRepititions = 1; 
        storedPixel = curPixel; 
       } 
       else 
       { 
        curPixelRepititions++; 
       } 
      } 

      if (curPixelRepititions > 0) 
      { 
       result.Add(Convert.ToByte(curPixelRepititions)); 
       result.Add(storedPixel.Value); 
      } 

      if (rowIndex == 0) 
      { 
       // EOF flag 
       result.Add(0x00); 
       result.Add(0x01); 
      } 
      else 
      { 
       // End of Line Flag 
       result.Add(0x00); 
       result.Add(0x00); 
      } 
     } 

     bmp.UnlockBits(data); 

     var paletteSize = (uint)bmp.Palette.Entries.Length * 4; 
     var header = new RleCompressedBmpHeader(); 
     header.HeaderSize = 40; 
     header.Size = header.HeaderSize + paletteSize + (uint)result.Count + 14; 
     header.Offset = header.HeaderSize + 14 + paletteSize; // total header size + palette size 
     header.Width = bmp.Width; 
     header.Height = bmp.Height; 
     header.Planes = 1; 
     header.BitCount = BitCount.Max256Colors; 
     // as far as I can tell, PixelsPerMeter are not terribly important 
     header.XPixelsPerMeter = 0x10000000; 
     header.YPixelsPerMeter = 0x10000000; 
     header.Compression = Compression.BI_RLE8; 
     header.ColorUsed = 256; 
     header.ColorImportant = 0; // use all available colors 
     header.ImageSize = header.HeaderSize + (uint)result.Count; 

     var headerBytes = header.ToBytes(); 
     var paletteBytes = ConvertPaletteToBytes(bmp.Palette); 

     return headerBytes.Concat(paletteBytes).Concat(result).ToArray(); 
    } 

    private byte[] ConvertPaletteToBytes(ColorPalette colorPalette) 
    { 
     return colorPalette.Entries.SelectMany(c => new byte[] 
      { 
       c.B, 
       c.G, 
       c.R, 
       0 
      }).ToArray(); 
    } 
+0

尽管此链接可能会回答问题,但最好在此处包含答案的重要部分,并提供供参考的链接。如果链接页面更改,则仅链接答案可能会失效。 –

+1

@NiklasRingdahl够公平的,我已经添加了我在其他答案中提供的代码示例。 –