-
Notifications
You must be signed in to change notification settings - Fork 6k
Added wide-gamut color support for ui.Image.toByteData
and ui.Image.colorSpace
#40031
Changes from all commits
950dece
a2cfc12
8922623
be01ffa
5e89eb8
d825503
b336fdd
e832a2c
ad3c508
e6793e4
18ea9a0
4c6d50b
8cd2b1c
5c49716
87e175d
ed9446f
501ae6c
16032ed
c1aacb5
76be0f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1568,6 +1568,31 @@ class Paint { | |
} | ||
} | ||
|
||
/// The color space describes the colors that are available to an [Image]. | ||
/// | ||
/// This value can help decide which [ImageByteFormat] to use with | ||
/// [Image.toByteData]. Images that are in the [extendedSRGB] color space | ||
/// should use something like [ImageByteFormat.rawExtendedRgba128] so that | ||
/// colors outside of the sRGB gamut aren't lost. | ||
/// | ||
/// This is also the result of [Image.colorSpace]. | ||
/// | ||
/// See also: https://en.wikipedia.org/wiki/Color_space | ||
enum ColorSpace { | ||
gaaclarke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The sRGB color space. | ||
/// | ||
/// You may know this as the standard color space for the web or the color | ||
/// space of non-wide-gamut Flutter apps. | ||
/// | ||
/// See also: https://en.wikipedia.org/wiki/SRGB | ||
sRGB, | ||
/// A color space that is backwards compatible with sRGB but can represent | ||
/// colors outside of that gamut with values outside of [0..1]. In order to | ||
/// see the extended values an [ImageByteFormat] like | ||
/// [ImageByteFormat.rawExtendedRgba128] must be used. | ||
extendedSRGB, | ||
} | ||
|
||
/// The format in which image bytes should be returned when using | ||
/// [Image.toByteData]. | ||
// We do not expect to add more encoding formats to the ImageByteFormat enum, | ||
|
@@ -1591,6 +1616,21 @@ enum ImageByteFormat { | |
/// image may use a single 8-bit channel for each pixel. | ||
rawUnmodified, | ||
|
||
/// Raw extended range RGBA format. | ||
/// | ||
/// Unencoded bytes, in RGBA row-primary form with straight alpha, 32 bit | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is straight alpha premultiplied or un-premultiplied (post multiplied)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's un-premultiplied, "straight" is the nomenclature we use in the other fields. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this matches the encoding of the dart:ui Color class, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The dart:ui Color class is using integer values for components, this is float32. |
||
/// float (IEEE 754 binary32) per channel. | ||
/// | ||
/// Example usage: | ||
/// | ||
/// ```dart | ||
/// final ByteData data = | ||
/// (await image.toByteData(format: ImageByteFormat.rawExtendedRgba128))!; | ||
/// final Float32List floats = Float32List.view(data.buffer); | ||
/// print('r:${floats[0]} g:${floats[1]} b:${floats[2]} a:${floats[3]}'); | ||
/// ``` | ||
rawExtendedRgba128, | ||
|
||
/// PNG format. | ||
/// | ||
/// A loss-less compression format for images. This format is well suited for | ||
|
@@ -1726,6 +1766,10 @@ class Image { | |
/// The [format] argument specifies the format in which the bytes will be | ||
/// returned. | ||
/// | ||
/// Using [ImageByteFormat.rawRgba] on an image in the color space | ||
/// [ColorSpace.extendedSRGB] will result in the gamut being squished to fit | ||
/// into the sRGB gamut, resulting in the loss of wide-gamut colors. | ||
/// | ||
/// Returns a future that completes with the binary image data or an error | ||
/// if encoding fails. | ||
// We do not expect to add more encoding formats to the ImageByteFormat enum, | ||
|
@@ -1737,6 +1781,29 @@ class Image { | |
return _image.toByteData(format: format); | ||
} | ||
|
||
/// The color space that is used by the [Image]'s colors. | ||
/// | ||
/// This value is a consequence of how the [Image] has been created. For | ||
/// example, loading a PNG that is in the Display P3 color space will result | ||
/// in a [ColorSpace.extendedSRGB] image. | ||
/// | ||
/// On rendering backends that don't support wide gamut colors (anything but | ||
/// iOS impeller), wide gamut images will still report [ColorSpace.sRGB] if | ||
/// rendering wide gamut colors isn't supported. | ||
// Note: The docstring will become outdated as new platforms support wide | ||
// gamut color, please keep it up to date. | ||
ColorSpace get colorSpace { | ||
final int colorSpaceValue = _image.colorSpace; | ||
switch (colorSpaceValue) { | ||
case 0: | ||
return ColorSpace.sRGB; | ||
case 1: | ||
return ColorSpace.extendedSRGB; | ||
default: | ||
throw UnsupportedError('Unrecognized color space: $colorSpaceValue'); | ||
} | ||
} | ||
|
||
/// If asserts are enabled, returns the [StackTrace]s of each open handle from | ||
/// [clone], in creation order. | ||
/// | ||
|
@@ -1903,6 +1970,9 @@ class _Image extends NativeFieldWrapperClass1 { | |
|
||
final Set<Image> _handles = <Image>{}; | ||
|
||
@Native<Int32 Function(Pointer<Void>)>(symbol: 'Image::colorSpace') | ||
external int get colorSpace; | ||
|
||
@override | ||
String toString() => '[$width\u00D7$height]'; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -351,6 +351,8 @@ abstract class Image { | |
|
||
List<StackTrace>? debugGetOpenHandleStackTraces() => null; | ||
|
||
ColorSpace get colorSpace => ColorSpace.sRGB; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like all concrete subclasses are implementing this getter. I think this means that the getter in the base class can be abstract. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason all of the other places had to implement it themselves is because they are using |
||
|
||
@override | ||
String toString() => '[$width\u00D7$height]'; | ||
} | ||
|
@@ -431,6 +433,11 @@ class ImageFilter { | |
engine.renderer.composeImageFilters(outer: outer, inner: inner); | ||
} | ||
|
||
enum ColorSpace { | ||
sRGB, | ||
extendedSRGB, | ||
} | ||
|
||
enum ImageByteFormat { | ||
rawRgba, | ||
rawStraightRgba, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, use stronger phrasing. Consider something like:
"An image in the extendedSRGB color space must use ImageByteFormat.rawExtendedRgba128 to avoid losing colors. Using ImageByteFormat blah blah will cause loss of color information" And define what loss of color information means if its a well defined process
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, reworded and I think that information would be better to be situated at
toByteData
so I added it there.