[FEATURE] Expose API for registering new codecs
Is your feature request related to a problem?
I want to add new image codecs for image formats which are not supported by the built-in Skia codecs. This is not currently possible, even though Skia itself supports it. There is no C# API and the C++ API has been excluded from the native library.
Describe the solution you would like
The native API, SkCodec::Register, requires a pointer to a C++ object.
Since this is not easy to wrap in C#, it would be acceptable just to expose the native API in your build of the native Skia library. We can then write and register a codec ourselves in a small native library.
Describe alternatives you have considered
There are no alternatives.
Additional context
No response
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
I dug into things and found that the non-exported method isn't a decision made by the SkiaSharp team, but a result of the mono/skia repo missing this upsteam bug fix: google/skia@ec70dfbaa2e99ae34cc40bebe7c59e3b87d31eaf
How would the perfect C# API look?
In my tired brain, it came up with this:
void Test()
{
RegisterDecoder("png", new PngDecoder());
}
void RegisterDecoder(string id, ISKCodecDecoder decoder)
{
}
interface ISKCodecDecoder
{
bool CanDecode(ReadOnlySpan<byte> data);
SKCodec Decode(SKStream stream, out SKCodecResult result);
}
Not sure we need to expose the exact same API that skia uses.
Looking at the code for SKCodec, there are quite a few things that we will need to first expose before you can possibly inherit from SKCodec.
Our use case also requires encoding. It's reasonable to separate the two interfaces, but having them merged and throwing NotSupportedException is also OK.
A way to retrieve a codec by name is required, since the current Encode methods use the SKEncodedImageFormat enum, which cannot be extended. We don't need to do anything special for the decoder as Skia automatically finds the correct one.
The ideal interface would bypass the SKCodec type entirely, and just convert between a stream and an image, or vice versa. However there are a lot of fiddly methods on the native SKCodec class, and some (or even most) may need to be implemented in C# too.
public static class SKCodec
{
public static ISKCodecEncoder GetEncoder(string extension) {}
void RegisterEncoder(string id, ISKCodecEncoder encoder) {}
void RegisterDecoder(string id, ISKCodecDecoder decoder) {}
public static void Encode(this SKImage, string extension, SKStream destination) {}
public static SKData Encode(this SKImage image, string extension) {}
}
interface ISKCodecEncoder
{
void Encode(SKImage image, SKStream destination);
SKData Encode(SKImage image);
}
interface ISKCodecDecoder
{
bool CanDecode(ReadOnlySpan<byte> data);
SKImage Decode(SKStream source);
}