mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.51k stars 537 forks source link

HEIF / HEIC file support #2887

Open SeppPenner opened 5 months ago

SeppPenner commented 5 months ago

Is your feature request related to a problem?

I wanted to load images on Android (Net Maui) from the gallery, but it only seems to be working with .JPEG images, not .HEIC images (I didn't test .PNG and that kind of stuff yet).

Describe the solution you would like

I would like to be able to decode heic images to a SKBitmap object.

Describe alternatives you have considered

None, I hace no idea how this could be done.

Additional context

Feature request https://github.com/mono/SkiaSharp/issues/1700 was close because of license issues, https://stackoverflow.com/questions/61232393/open-heic-bitmap-file-in-skiasharp states that it isn't supported.

Code of Conduct

SeppPenner commented 5 months ago

I worked around this by following https://learn.microsoft.com/en-us/answers/questions/1282529/does-anybody-know-if-maui-has-a-utility-or-package:

#if ANDROID
global using AndroidBitmapFactory = Android.Graphics.BitmapFactory;
global using AndroidBitmap = Android.Graphics.Bitmap;
#endif

#if IOS
global using IoSNSData = Foundation.NSData;
global using IoSUIImage = UIKit.UIImage;
#endif

public static class ImageHelper
{
    try
    {
        var originalFilePath = await SavePhotoToCache(photo);

        // Don't convert a JPG / JPEG file.
        var filePathLowerCase = originalFilePath.ToLowerInvariant();

        if (filePathLowerCase.EndsWith(".jpg") || filePathLowerCase.EndsWith(".jpeg"))
        {
            return originalFilePath;
        }

        var jpegFilePath = Path.Combine(FileSystem.CacheDirectory, $"{Path.GetFileNameWithoutExtension(photo.FileName)}.jpg");
#if IOS
        using var data = IoSNSData.FromFile(originalFilePath);
        using var image = IoSUIImage.LoadFromData(data);

        if (image is null)
        {
            return null;
        }

        using var jpegData = image.AsJPEG();

        if (jpegData is null)
        {
            return null;
        }

        if (jpegData.Save(jpegFilePath, false, out var error))
        {
            FileHelper.TryDeleteFile(originalFilePath);
            return jpegFilePath;
        }
        else
        {
            return null;
        }
#endif

#if ANDROID
        var image = await AndroidBitmapFactory.DecodeFileAsync(originalFilePath);
        var compressionFormat = AndroidBitmap.CompressFormat.Jpeg;

        if (image is null || compressionFormat is null)
        {
            return null;
        }

        using var ms = new MemoryStream();
        image.Compress(compressionFormat, 100, ms);
        await File.WriteAllBytesAsync(jpegFilePath, ms.ToArray());
        FileHelper.TryDeleteFile(originalFilePath);
        return jpegFilePath;
#endif
    }
    catch
    {
        return null;
    }
}

The code takes the photo that comes from the MAUI media picker or camera as FileResult, saves it to the cache directory first and afterwards tries to convert and save the file as JPG format. I have tested this on Android (Not yet for iOS), and it works with .heic files.

Note: I have left the deletion of the cached file(s) here for simplicity reasons. In my code, the files get deleted properly once not needed anymore, of course.

EDIT: The new version is tested with iOS as well now.