Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] SKBitmap.GetPixel ignores color space whereas SetPixel respects it #2354

Open
koszeggy opened this issue Jan 5, 2023 · 0 comments
Open

Comments

@koszeggy
Copy link

koszeggy commented Jan 5, 2023

Disclaimer

I know that GetPixel is directly exposed by the Skia API, whereas there is no "official" SetPixel and it uses SKCanvas.DrawPoint in the background. Still, it would be desirable for GetPixel to reverse the transformations that SetPixel performs (if the color type/space allows it).

Description

SKBitmap.SetPixel (and the drawing operations by SKCanvas that use SKColor) always treat SKColor as an unpremultiplied sRGB color and they perform every necessary transformation as per the color space, but SKBitmap.GetPixel ignores the SKImageInfo.ColorSpace property (respects only SKColorType and SKAlphaType) and may return a completely invalid color.

Code

private static object[] ColorSpaceTestSource { get; } =
{
    new object[] { SKColorType.Bgra8888, SKAlphaType.Unpremul, SKColorSpace.CreateSrgb() }, // OK
    new object[] { SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb() },   // OK
    new object[] { SKColorType.RgbaF16, SKAlphaType.Premul, SKColorSpace.CreateSrgb() },    // OK
    new object[] { SKColorType.Bgra8888, SKAlphaType.Unpremul, SKColorSpace.CreateSrgbLinear() }, // Fail
    new object[] { SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateRgb(SKColorSpaceTransferFn.Srgb, SKColorSpaceXyz.AdobeRgb) }, // Fail
};

[TestCaseSource(nameof(ColorSpaceTestSource))]
public void ColorSpaceTest(SKColorType colorType, SKAlphaType alphaType, SKColorSpace colorSpace)
{
    var testColor = new SKColor(red: 128, green: 255, blue: 64, alpha: 128);
    using var bitmap = new SKBitmap(new SKImageInfo(1, 1, colorType, alphaType, colorSpace));
    bitmap.SetPixel(0, 0, testColor);
    Assert.AreEqual(testColor, bitmap.GetPixel(0, 0));
}

Expected Behavior

GetPixel should return an unpremultiplied sRGB color even if the color space transfer function or color space XYZ is not sRGB.

Actual Behavior

GetPixel successfully unpremultiplies the color if needed but fails to convert linear gamma or any non-sRGB color space to sRGB.

Basic Information

  • Version with issue: 2.88.3
  • Last known good version: Don't know, maybe never worked
  • IDE: Visual Studio
  • Platform Target Frameworks:
    • Windows Classic: Windows 10/11
    • Linux: Ubuntu 22.04
  • Target Devices:
    • Google Pixel 5 (Android Emulator on Windows)

Known workaround

A very inefficient workaround is to create a 1x1 pixel sRGB Bgra8888/Unpremul bitmap, wrap it into a new SKCanvas, copy that single pixel area into the temp bitmap, and then read the result by GetPixel on the temp bitmap.

Possible solutions, API changes

I don't know if there is anyone who relies on the current behavior but maybe a new GetPixel(int x, int y, bool forceSrgb) overload could be introduced. Or a new GetPixelSrgb(int x, int y) method.

And I also don't know if this could be solved by calling some trivial API exposed by Skia, or it should be implemented at the C# side. Maybe for the default linear color space I could use the formula mentioned in the documentation but I have no idea how I should use any custom SKColorSpaceXyz coefficients (the documentation literally does not say anything about this) or what I could do if SKColorSpace.IsNumericalTransferFunction returns false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Development

No branches or pull requests

1 participant