4

Using Magick.NET-Q8-AnyCPU. I want to convert existing TIFF image to grayscale 8 bpp BMP image. I tried this:

byte[] input = <existing TIFF image>;
using (var image = new MagickImage(input))
{
    image.Grayscale();
    image.ColorType = ColorType.Palette;
    image.Depth = 8;
    image.Quantize(new QuantizeSettings() { Colors = 256,  DitherMethod = DitherMethod.No });

    byte[] result = image.ToByteArray(MagickFormat.Bmp);

    return result;
}

In FastStone Viewer the image is reported as 8-bit, HOWEVER in file Properties > Details the image is reported as Bit depth: 32. I need it to be 8 here. I can convert this image in Paint.NET, and when I choose there Bit Depth: 8-bit then new image will properly show 8-bit depth in file properties.

So, Paint.NET creates proper 8-bit bitmap. How to do it with Magick.NET?

Dialecticus
  • 16,400
  • 7
  • 43
  • 103

2 Answers2

2

It seems that's impossible. Neither image.Depth = 8 nor image.BitDepth(8) works. May be the root is in:

// ImageMagick.MagickImage.NativeMethods.X{ver.}
[DllImport("Magick.Native-Q8-x{ver.}.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MagickImage_SetBitDepth(IntPtr Instance, UIntPtr channels, UIntPtr value);

or

// ImageMagick.MagickImage.NativeMethods.X{ver.}
[DllImport("Magick.Native-Q8-x{ver.}.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MagickImage_WriteStream(IntPtr Instance, IntPtr settings, ReadWriteStreamDelegate writer, SeekStreamDelegate seeker, TellStreamDelegate teller, ReadWriteStreamDelegate reader, out IntPtr exception);

Looks like it can not create 8 bits .bmp, though there is no problem with .png.

var original = @"D:\tmp\0.tif";
var copy = @"D:\tmp\0.bmp";

using (var image = new MagickImage(original))
{
    image.Grayscale();
    image.ColorType = ColorType.Palette;
    image.Quantize(new QuantizeSettings() { Colors = 256, DitherMethod = DitherMethod.No });
    byte[] result = image.ToByteArray(MagickFormat.Png8);
    File.WriteAllBytes(copy, result);
}
Console.WriteLine("Press 'Enter'..."); // one have 8 bits .png here
Console.ReadLine();
using (var image = new MagickImage(copy))
{
    byte[] result = image.ToByteArray(MagickFormat.Bmp3);
    File.WriteAllBytes(copy, result);
} // but ends up with 32 bits .bmp again here

I also noticed that

image.Quantize(new QuantizeSettings() { Colors = 16, DitherMethod = DitherMethod.No });

produces 4 bits result. Gradual increasing gives 32 bits, but never 8 bits.

CSDev
  • 3,177
  • 6
  • 19
  • 37
2

Windows Explorer displays all compressed BMP files as 32-bit contrary to their actual bit depths.

I don't know it's a bug or not but I'm a little bit closer to call it a bug.

Because; after creating a 8bpp BMP file with your code, when I open the file with a binary editor, in the bitmap header struct I saw that the bits per pixel field value (blocks 28-29) was 8 as it must be. Also, the next byte 01 (offset 30) means that the data compressed with Run-length encoding which is a straightforward lossless data compression algorithm.

8bpp bmp hex

Therefore, I can say that there's no problem with the image you produced with Magick.NET, it's certainly an 8bpp BMP image file, but compressed.

Unlike Magick.NET's defaults, it seems that Paint.NET produce uncompressed BMP files, that's why you see different bit depths due to weirdness of Windows Explorer.

To fix this, you can disable compression so the bit depth value displayed in the properties dialog will be the value you expect.

image.Settings.Compression = CompressionMethod.NoCompression;
byte[] result = image.ToByteArray(MagickFormat.Bmp);
Kul-Tigin
  • 16,728
  • 1
  • 35
  • 64