Convert BMP to TIFF using System.Drawing.Common on Mac OS X

Viewed 858

I'm trying to convert a BMP image to a tiff image using System.Drawing.Common with the following C# code (.NET CORE 3.1) on Mac os:

        public void SaveBitmapAsTiff(string location, Bitmap image)
        {

            var imageCodecInfo = GetEncoderInfo("image/tiff");
            var encoder = Encoder.Compression;
            var encoderParameters = new EncoderParameters(1);
            
            var encoderParameter = new EncoderParameter(
                encoder,
                (long)EncoderValue.CompressionLZW);
            encoderParameters.Param[0] = encoderParameter;
            location = location.Replace(".bmp", "");
            image.Save(location + ".tiff", imageCodecInfo, encoderParameters);
            // image.Save(location + ".tiff", ImageFormat.Tiff); Also tried this method overload
        }
        
        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;
        }

the location parameter contains the file path we want to write to, and the image parameter contains the bitmap we loaded from the disk.

When I try to run this code I get the following exception:

Unhandled exception. System.NotImplementedException: Not implemented.
   at System.Drawing.SafeNativeMethods.Gdip.CheckStatus(Int32 status)
   at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
   at System.Drawing.Image.Save(String filename, ImageFormat format)
   at BmpTiffConverter.Services.FileService.SaveBitmapAsTiff(String location, Bitmap image) in /Users/tomcoldenhoff/Documents/ReSoftware/Repositories/BmpTiffConverter/BmpTiffConverter/Services/FileService.cs:line 54
   at BmpTiffConverter.Services.ConverterService.ConvertBmpToTiff(String inputPath, String outputPath) in /Users/tomcoldenhoff/Documents/ReSoftware/Repositories/BmpTiffConverter/BmpTiffConverter/Services/ConverterService.cs:line 20

My google searches suggested that I had to install libgdiplus which I already did with brew install mono-libgdiplus and brew upgrade mono-libgdiplus.

I've also tried @Andy's suggested answer by installing the runtime.osx.10.10-x64.CoreCompat.System.Drawing package, unfortunately this didn't work.

Does anyone have another suggestion?

2 Answers

The issue is that mono-libgdiplus doesn't implement EncoderValue.CompressionLZW encoder parameter. I would suggest not using that library, and instead install this nuget package:

mac os libgdiplus

runtime.osx.10.10-x64.CoreCompat.System.Drawing

Once I added that package, your code worked perfectly.

Unfortunately I do not have a Mac to test. I tested your code on Windows and Ubuntu 20 with DotNet Core 3.1 and your code seems to work. I can only guess it is something to do with how you create or modify your Bitmap potentially.

Below is how I created the project under Linux. dotnet add package is how I added the System.Drawing.Common NuGet package. I'm not sure how it works on Mono or if you are using Mono.

> dotnet new console
> dotnet add package System.Drawing.Common
> dotnet restore

I edited program.cs

> dotnet build
> dotnet run

and it ran perfectly fine. I can now open the tiff image in any image viewer.

From a code style point of view there are quite a few changes I would recommend. I know you followed MSDN's code examples, but as it stands their examples are not very clear in this instance. I've just copied and pasted my whole .cs file and added comments to the various modifications. Writing clear code is just as important as writing working code.

using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

class Program
{
    // made the function more generic Bitmap inherits from Image and Image contains
    // the save feature so it might as well be made more general
    public static void SaveImageAsTiff(string location, Image image)
    {
        // fetch tiffCodec
        var tiffCodec = ImageCodecInfo
            .GetImageEncoders()
            .Where(info => info.MimeType == "image/tiff")
            .First();

        // create encoderParameters
        // this structure is much clearer IMO
        // (Can you see that there is now a color depth parameter?)
        var encoderParameters = new EncoderParameters(2) 
        {
            Param = new EncoderParameter[]{
                new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW),
                new EncoderParameter(Encoder.ColorDepth, 24L)
            }
        };

        // save image using codec and encoder parameters
        // it is common behavior to just save the file with the given location parameter 
        // and not to try do some extension conversion
        image.Save(location, tiffCodec, encoderParameters);
    }

    static void Main(string[] args)
    {
        // original bitmap location
        // (Yes forward slashes work on Windows so relative file paths are simple to handle)
        string bmpLocation = @"C:/Images/test.bmp";
        //string bmpLocation = @"~/Images/test.bmp"; // test on Linux

        // tidy way to convert file extensions of a path in C#
        string tiffLocation = System.IO.Path.ChangeExtension(bmpLocation, "tiff");

        // open image and force it to be a bitmap for examples sakes
        Bitmap bmpImage = new Bitmap(Image.FromFile(bmpLocation));

        // save image as tiff
        SaveImageAsTiff(tiffLocation, bmpImage);
    }
}
Related