mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Adds support for r32float textures (#178418)
fixes https://github.com/flutter/flutter/issues/178420 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
f3415ecb66
commit
d8fc22e25c
@ -73,6 +73,20 @@ void main() {
|
||||
|
||||
group('end-to-end test', () {
|
||||
testWidgets('renders sdfs with rgba32f', (WidgetTester tester) async {
|
||||
app.gTargetPixelFormat = ui.TargetPixelFormat.rgbaFloat32;
|
||||
app.main();
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
await _getScreenshot();
|
||||
// TODO(gaaclarke): Turn this into a golden test. This turned out to be
|
||||
// quite involved so it's deferred.
|
||||
// expect(
|
||||
// screenshot,
|
||||
// matchesGoldenFile('high_bitrate_images.rbga32f'),
|
||||
// );
|
||||
});
|
||||
|
||||
testWidgets('renders sdfs with r32f', (WidgetTester tester) async {
|
||||
app.gTargetPixelFormat = ui.TargetPixelFormat.rFloat32;
|
||||
app.main();
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
await _getScreenshot();
|
||||
|
||||
@ -9,6 +9,8 @@ import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
ui.TargetPixelFormat gTargetPixelFormat = ui.TargetPixelFormat.rFloat32;
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
@ -55,11 +57,22 @@ class _SdfCanvasState extends State<SdfCanvas> {
|
||||
_shader = shader;
|
||||
});
|
||||
});
|
||||
_loadSdfImage().then((ui.Image image) {
|
||||
setState(() {
|
||||
_sdfImage = image;
|
||||
});
|
||||
});
|
||||
switch (gTargetPixelFormat) {
|
||||
case ui.TargetPixelFormat.rgbaFloat32:
|
||||
_loadRGBA32FloatSdfImage().then((ui.Image image) {
|
||||
setState(() {
|
||||
_sdfImage = image;
|
||||
});
|
||||
});
|
||||
case ui.TargetPixelFormat.rFloat32:
|
||||
_loadR32FloatSdfImage().then((ui.Image image) {
|
||||
setState(() {
|
||||
_sdfImage = image;
|
||||
});
|
||||
});
|
||||
case ui.TargetPixelFormat.dontCare:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ui.FragmentShader> _loadShader() async {
|
||||
@ -67,7 +80,39 @@ class _SdfCanvasState extends State<SdfCanvas> {
|
||||
return program.fragmentShader();
|
||||
}
|
||||
|
||||
Future<ui.Image> _loadSdfImage() async {
|
||||
Future<ui.Image> _loadR32FloatSdfImage() async {
|
||||
const int width = 1024;
|
||||
const int height = 1024;
|
||||
const double radius = width / 4.0;
|
||||
final List<double> floats = List<double>.filled(width * height, 0.0);
|
||||
for (int i = 0; i < height; ++i) {
|
||||
for (int j = 0; j < width; ++j) {
|
||||
double x = j.toDouble();
|
||||
double y = i.toDouble();
|
||||
x -= width / 2.0;
|
||||
y -= height / 2.0;
|
||||
final double length = sqrt(x * x + y * y) - radius;
|
||||
final int idx = i * width + j;
|
||||
floats[idx] = length - radius;
|
||||
}
|
||||
}
|
||||
final Float32List floatList = Float32List.fromList(floats);
|
||||
final Uint8List intList = Uint8List.view(floatList.buffer);
|
||||
final Completer<ui.Image> completer = Completer<ui.Image>();
|
||||
ui.decodeImageFromPixels(
|
||||
intList,
|
||||
width,
|
||||
height,
|
||||
ui.PixelFormat.rFloat32,
|
||||
targetFormat: ui.TargetPixelFormat.rFloat32,
|
||||
(ui.Image image) {
|
||||
completer.complete(image);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<ui.Image> _loadRGBA32FloatSdfImage() async {
|
||||
const int width = 1024;
|
||||
const int height = 1024;
|
||||
const double radius = width / 4.0;
|
||||
|
||||
@ -110,6 +110,7 @@ enum class PixelFormat : uint8_t {
|
||||
kB10G10R10XR,
|
||||
kB10G10R10XRSRGB,
|
||||
kB10G10R10A10XR,
|
||||
kR32Float,
|
||||
// Depth and stencil formats.
|
||||
kS8UInt,
|
||||
kD24UnormS8Uint,
|
||||
@ -171,6 +172,8 @@ constexpr const char* PixelFormatToString(PixelFormat format) {
|
||||
return "D24UnormS8Uint";
|
||||
case PixelFormat::kD32FloatS8UInt:
|
||||
return "D32FloatS8UInt";
|
||||
case PixelFormat::kR32Float:
|
||||
return "R32Float";
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
@ -479,6 +482,7 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) {
|
||||
case PixelFormat::kB8G8R8A8UNormIntSRGB:
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return 4u;
|
||||
case PixelFormat::kD24UnormS8Uint:
|
||||
return 4u;
|
||||
|
||||
@ -178,6 +178,11 @@ struct TexImage2DData {
|
||||
external_format = GL_RGBA;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case PixelFormat::kR32Float:
|
||||
internal_format = GL_RED;
|
||||
external_format = GL_RED;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case PixelFormat::kR16G16B16A16Float:
|
||||
internal_format = GL_RGBA;
|
||||
external_format = GL_RGBA;
|
||||
|
||||
@ -40,6 +40,7 @@ static bool IsDepthStencilFormat(PixelFormat format) {
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return false;
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
@ -87,6 +88,11 @@ struct TexImage2DData {
|
||||
external_format = GL_RGBA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case PixelFormat::kR32Float:
|
||||
internal_format = GL_R32F;
|
||||
external_format = GL_RGBA;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
case PixelFormat::kR32G32B32A32Float:
|
||||
internal_format = GL_RGBA32F;
|
||||
external_format = GL_RGBA;
|
||||
@ -401,6 +407,7 @@ static std::optional<GLenum> ToRenderBufferFormat(PixelFormat format) {
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return std::nullopt;
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
|
||||
@ -107,6 +107,8 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) {
|
||||
return SafeMTLPixelFormatBGR10_XR();
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
return SafeMTLPixelFormatBGRA10_XR();
|
||||
case PixelFormat::kR32Float:
|
||||
return MTLPixelFormatR32Float;
|
||||
}
|
||||
return MTLPixelFormatInvalid;
|
||||
};
|
||||
|
||||
@ -175,6 +175,8 @@ constexpr vk::Format ToVKImageFormat(PixelFormat format) {
|
||||
return vk::Format::eR8Unorm;
|
||||
case PixelFormat::kR8G8UNormInt:
|
||||
return vk::Format::eR8G8Unorm;
|
||||
case PixelFormat::kR32Float:
|
||||
return vk::Format::eR32Sfloat;
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
@ -426,6 +428,7 @@ constexpr bool PixelFormatIsDepthStencil(PixelFormat format) {
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return false;
|
||||
case PixelFormat::kS8UInt:
|
||||
case PixelFormat::kD24UnormS8Uint:
|
||||
@ -525,6 +528,7 @@ constexpr vk::ImageAspectFlags ToVKImageAspectFlags(PixelFormat format) {
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return vk::ImageAspectFlagBits::eColor;
|
||||
case PixelFormat::kS8UInt:
|
||||
return vk::ImageAspectFlagBits::eStencil;
|
||||
@ -599,6 +603,7 @@ constexpr vk::ImageAspectFlags ToImageAspectFlags(PixelFormat format) {
|
||||
case PixelFormat::kB10G10R10XR:
|
||||
case PixelFormat::kB10G10R10XRSRGB:
|
||||
case PixelFormat::kB10G10R10A10XR:
|
||||
case PixelFormat::kR32Float:
|
||||
return vk::ImageAspectFlagBits::eColor;
|
||||
case PixelFormat::kS8UInt:
|
||||
return vk::ImageAspectFlagBits::eStencil;
|
||||
|
||||
@ -104,6 +104,9 @@ constexpr FlutterGPUPixelFormat FromImpellerPixelFormat(
|
||||
switch (value) {
|
||||
case impeller::PixelFormat::kUnknown:
|
||||
return FlutterGPUPixelFormat::kUnknown;
|
||||
case impeller::PixelFormat::kR32Float:
|
||||
FML_DCHECK(false) << "k32Float not implemented.";
|
||||
return FlutterGPUPixelFormat::kUnknown;
|
||||
case impeller::PixelFormat::kA8UNormInt:
|
||||
return FlutterGPUPixelFormat::kA8UNormInt;
|
||||
case impeller::PixelFormat::kR8UNormInt:
|
||||
|
||||
@ -1909,6 +1909,9 @@ enum PixelFormat {
|
||||
/// component, followed by: green, blue and alpha. Premultiplied alpha isn't
|
||||
/// used, matching [ImageByteFormat.rawExtendedRgba128].
|
||||
rgbaFloat32,
|
||||
|
||||
/// Each pixel is 32 bits, the red channel is just one 32 bit float.
|
||||
rFloat32,
|
||||
}
|
||||
|
||||
/// The format of pixel data of the texture generated by
|
||||
@ -1919,6 +1922,9 @@ enum TargetPixelFormat {
|
||||
|
||||
/// Each pixel is 128 bits, where each color component is a 32 bit float.
|
||||
rgbaFloat32,
|
||||
|
||||
/// Each pixel is 32 bits, the red channel is just one 32 bit float.
|
||||
rFloat32,
|
||||
}
|
||||
|
||||
/// Signature for [Image] lifecycle events.
|
||||
|
||||
@ -40,6 +40,7 @@ class ImageDecoder {
|
||||
/// Explicitly declare the target pixel is left for the engine to decide.
|
||||
kDontCare,
|
||||
kR32G32B32A32Float,
|
||||
kR32Float,
|
||||
};
|
||||
|
||||
struct Options {
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include "flutter/fml/closure.h"
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "flutter/impeller/core/allocator.h"
|
||||
#include "flutter/impeller/display_list/dl_image_impeller.h"
|
||||
@ -262,6 +263,26 @@ absl::StatusOr<ImageDecoderImpeller::DecompressResult> ResizeOnCpu(
|
||||
.image_info = decoded_image_info.value()};
|
||||
}
|
||||
|
||||
bool IsZeroOpConversion(ImageDescriptor::PixelFormat input,
|
||||
ImageDecoder::TargetPixelFormat output) {
|
||||
switch (input) {
|
||||
case ImageDescriptor::PixelFormat::kR32Float:
|
||||
return output == ImageDecoder::TargetPixelFormat::kR32Float;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
impeller::PixelFormat ToImpellerPixelFormat(
|
||||
ImageDecoder::TargetPixelFormat format) {
|
||||
switch (format) {
|
||||
case ImageDecoder::TargetPixelFormat::kR32Float:
|
||||
return impeller::PixelFormat::kR32Float;
|
||||
default:
|
||||
FML_DCHECK(false) << "Unsupported pixel format";
|
||||
return impeller::PixelFormat::kUnknown;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<impeller::PixelFormat> ImageDecoderImpeller::ToPixelFormat(
|
||||
@ -321,12 +342,41 @@ ImageDecoderImpeller::DecompressTexture(
|
||||
return absl::InvalidArgumentError(decode_error);
|
||||
}
|
||||
|
||||
const SkISize source_size = descriptor->image_info().dimensions();
|
||||
const SkISize source_size = SkISize::Make(descriptor->image_info().width,
|
||||
descriptor->image_info().height);
|
||||
const SkISize target_size =
|
||||
SkISize::Make(std::min(max_texture_size.width,
|
||||
static_cast<int64_t>(options.target_width)),
|
||||
std::min(max_texture_size.height,
|
||||
static_cast<int64_t>(options.target_height)));
|
||||
|
||||
// Fast path for when the input requires no decompressing or conversion.
|
||||
if (!descriptor->is_compressed() && source_size == target_size &&
|
||||
IsZeroOpConversion(descriptor->image_info().format,
|
||||
options.target_format)) {
|
||||
impeller::DeviceBufferDescriptor desc;
|
||||
desc.storage_mode = impeller::StorageMode::kHostVisible;
|
||||
desc.size = descriptor->data()->size();
|
||||
std::shared_ptr<impeller::DeviceBuffer> buffer =
|
||||
allocator->CreateBuffer(desc);
|
||||
if (!buffer) {
|
||||
return absl::ResourceExhaustedError("Could not create buffer for image.");
|
||||
}
|
||||
sk_sp<SkData> data = descriptor->data();
|
||||
memcpy(buffer->OnGetContents(), data->bytes(), data->size());
|
||||
buffer->Flush();
|
||||
|
||||
return ImageDecoderImpeller::DecompressResult{
|
||||
.device_buffer = std::move(buffer),
|
||||
.image_info =
|
||||
{
|
||||
.size =
|
||||
impeller::ISize(source_size.width(), source_size.height()),
|
||||
.format = ToImpellerPixelFormat(options.target_format),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
SkISize decode_size = source_size;
|
||||
if (descriptor->is_compressed()) {
|
||||
decode_size = descriptor->get_scaled_dimensions(std::max(
|
||||
@ -334,7 +384,8 @@ ImageDecoderImpeller::DecompressTexture(
|
||||
static_cast<float>(target_size.height()) / source_size.height()));
|
||||
}
|
||||
|
||||
const SkImageInfo& base_image_info = descriptor->image_info();
|
||||
const SkImageInfo base_image_info =
|
||||
ImageDescriptor::ToSkImageInfo(descriptor->image_info());
|
||||
const absl::StatusOr<SkImageInfo> image_info = CreateImageInfo(
|
||||
base_image_info, decode_size, supports_wide_gamut, options.target_format);
|
||||
|
||||
|
||||
@ -221,9 +221,12 @@ TEST(ImageDecoderNoGLTest, ImpellerRGBA32FDecode) {
|
||||
fml::MakeRefCounted<ImmutableBuffer>(std::move(sk_data));
|
||||
|
||||
// 2. Create an ImageDescriptor using the private constructor.
|
||||
SkImageInfo image_info =
|
||||
SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kPremul_SkAlphaType,
|
||||
SkColorSpace::MakeSRGB());
|
||||
ImageDescriptor::ImageInfo image_info = {
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.format = ImageDescriptor::PixelFormat::kRGBAFloat32,
|
||||
.alpha_type = kUnpremul_SkAlphaType,
|
||||
};
|
||||
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
|
||||
immutable_buffer->data(), image_info, sizeof(pixel_data));
|
||||
|
||||
@ -265,6 +268,63 @@ TEST(ImageDecoderNoGLTest, ImpellerRGBA32FDecode) {
|
||||
#endif // IMPELLER_SUPPORTS_RENDERING
|
||||
}
|
||||
|
||||
TEST(ImageDecoderNoGLTest, ImpellerR32FDecode) {
|
||||
#if defined(OS_FUCHSIA)
|
||||
GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
|
||||
#endif
|
||||
|
||||
#if !IMPELLER_SUPPORTS_RENDERING
|
||||
GTEST_SKIP() << "test only supported on impeller";
|
||||
#else
|
||||
// 1. Create a 1x1 pixel with float RGBA values.
|
||||
float pixel_data[] = {1.0f};
|
||||
sk_sp<SkData> sk_data = SkData::MakeWithCopy(pixel_data, sizeof(pixel_data));
|
||||
auto immutable_buffer =
|
||||
fml::MakeRefCounted<ImmutableBuffer>(std::move(sk_data));
|
||||
|
||||
// 2. Create an ImageDescriptor using the private constructor.
|
||||
ImageDescriptor::ImageInfo image_info = {
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.format = ImageDescriptor::PixelFormat::kR32Float,
|
||||
.alpha_type = kUnpremul_SkAlphaType,
|
||||
};
|
||||
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
|
||||
immutable_buffer->data(), image_info, sizeof(pixel_data));
|
||||
|
||||
// Set up Impeller capabilities and allocator.
|
||||
std::shared_ptr<impeller::Capabilities> capabilities =
|
||||
impeller::CapabilitiesBuilder()
|
||||
.SetSupportsTextureToTextureBlits(true)
|
||||
.Build();
|
||||
std::shared_ptr<impeller::Allocator> allocator =
|
||||
std::make_shared<impeller::TestImpellerAllocator>();
|
||||
|
||||
// 3. Call ImageDecoderImpeller::DecompressTexture with this ImageDescriptor.
|
||||
absl::StatusOr<ImageDecoderImpeller::DecompressResult> result =
|
||||
ImageDecoderImpeller::DecompressTexture(
|
||||
descriptor.get(),
|
||||
/*options=*/
|
||||
{.target_width = 1,
|
||||
.target_height = 1,
|
||||
.target_format = ImageDecoder::TargetPixelFormat::kR32Float},
|
||||
/*max_texture_size=*/{1, 1},
|
||||
/*supports_wide_gamut=*/true, capabilities, allocator);
|
||||
|
||||
// 4. Assert that wide_result->image_info.format is
|
||||
// impeller::PixelFormat::kR32G32B32A32Float.
|
||||
ASSERT_TRUE(result.ok());
|
||||
ASSERT_EQ(result->image_info.format, impeller::PixelFormat::kR32Float);
|
||||
|
||||
// Optionally, verify the pixel data if needed.
|
||||
const float* decompressed_pixel_ptr =
|
||||
reinterpret_cast<const float*>(result->device_buffer->OnGetContents());
|
||||
ASSERT_NE(decompressed_pixel_ptr, nullptr);
|
||||
EXPECT_EQ(decompressed_pixel_ptr[0], 1.0f);
|
||||
|
||||
#endif // IMPELLER_SUPPORTS_RENDERING
|
||||
}
|
||||
|
||||
TEST(ImageDecoderNoGLTest, ImpellerUnmultipliedAlphaPng) {
|
||||
#if defined(OS_FUCHSIA)
|
||||
GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
|
||||
|
||||
@ -83,7 +83,8 @@ static sk_sp<SkImage> ImageFromDecompressedData(
|
||||
TRACE_EVENT0("flutter", __FUNCTION__);
|
||||
flow.Step(__FUNCTION__);
|
||||
auto image = SkImages::RasterFromData(
|
||||
descriptor->image_info(), descriptor->data(), descriptor->row_bytes());
|
||||
ImageDescriptor::ToSkImageInfo(descriptor->image_info()),
|
||||
descriptor->data(), descriptor->row_bytes());
|
||||
|
||||
if (!image) {
|
||||
FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
|
||||
@ -113,7 +114,9 @@ sk_sp<SkImage> ImageDecoderSkia::ImageFromCompressedData(
|
||||
return image ? image->makeRasterImage(nullptr) : nullptr;
|
||||
}
|
||||
|
||||
const SkISize source_dimensions = descriptor->image_info().dimensions();
|
||||
const SkImageInfo image_info =
|
||||
ImageDescriptor::ToSkImageInfo(descriptor->image_info());
|
||||
const SkISize source_dimensions = image_info.dimensions();
|
||||
const SkISize resized_dimensions = {static_cast<int32_t>(target_width),
|
||||
static_cast<int32_t>(target_height)};
|
||||
|
||||
@ -126,8 +129,7 @@ sk_sp<SkImage> ImageDecoderSkia::ImageFromCompressedData(
|
||||
// If the codec supports efficient sub-pixel decoding, decoded at a resolution
|
||||
// close to the target resolution before resizing.
|
||||
if (decode_dimensions != source_dimensions) {
|
||||
auto scaled_image_info =
|
||||
descriptor->image_info().makeDimensions(decode_dimensions);
|
||||
auto scaled_image_info = image_info.makeDimensions(decode_dimensions);
|
||||
|
||||
SkBitmap scaled_bitmap;
|
||||
if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
|
||||
|
||||
@ -459,7 +459,8 @@ TEST_F(ImageDecoderFixtureTest, ImpellerNullColorspace) {
|
||||
EXPECT_EQ(nullptr, image->colorSpace());
|
||||
|
||||
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
|
||||
std::move(data), image->imageInfo(), 10 * 4);
|
||||
std::move(data), ImageDescriptor::CreateImageInfo(image->imageInfo()),
|
||||
10 * 4);
|
||||
|
||||
#if IMPELLER_SUPPORTS_RENDERING
|
||||
std::shared_ptr<impeller::Capabilities> capabilities =
|
||||
@ -492,7 +493,8 @@ TEST_F(ImageDecoderFixtureTest, ImpellerPixelConversion32F) {
|
||||
EXPECT_EQ(nullptr, image->colorSpace());
|
||||
|
||||
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
|
||||
std::move(data), image->imageInfo(), 10 * 16);
|
||||
std::move(data), ImageDescriptor::CreateImageInfo(image->imageInfo()),
|
||||
10 * 16);
|
||||
|
||||
#if IMPELLER_SUPPORTS_RENDERING
|
||||
std::shared_ptr<impeller::Capabilities> capabilities =
|
||||
|
||||
@ -17,24 +17,78 @@ namespace flutter {
|
||||
|
||||
IMPLEMENT_WRAPPERTYPEINFO(ui, ImageDescriptor);
|
||||
|
||||
const SkImageInfo ImageDescriptor::CreateImageInfo() const {
|
||||
FML_DCHECK(generator_);
|
||||
return generator_->GetInfo();
|
||||
ImageDescriptor::ImageInfo ImageDescriptor::CreateImageInfo(
|
||||
const SkImageInfo& sk_image_info) {
|
||||
PixelFormat format;
|
||||
switch (sk_image_info.colorType()) {
|
||||
case kUnknown_SkColorType:
|
||||
format = kUnknown;
|
||||
break;
|
||||
case kRGBA_8888_SkColorType:
|
||||
format = kRGBA8888;
|
||||
break;
|
||||
case kBGRA_8888_SkColorType:
|
||||
format = kBGRA8888;
|
||||
break;
|
||||
case kRGBA_F32_SkColorType:
|
||||
format = kRGBAFloat32;
|
||||
break;
|
||||
case kGray_8_SkColorType:
|
||||
format = kGray8;
|
||||
break;
|
||||
default:
|
||||
FML_DCHECK(false) << "Unsupported pixel format: "
|
||||
<< sk_image_info.colorType();
|
||||
format = kRGBA8888;
|
||||
}
|
||||
return ImageInfo{
|
||||
.width = static_cast<uint32_t>(sk_image_info.width()),
|
||||
.height = static_cast<uint32_t>(sk_image_info.height()),
|
||||
.format = format,
|
||||
.alpha_type = sk_image_info.alphaType(),
|
||||
.color_space = sk_image_info.refColorSpace(),
|
||||
};
|
||||
}
|
||||
|
||||
SkImageInfo ImageDescriptor::ToSkImageInfo(const ImageInfo& image_info) {
|
||||
SkColorType color_type = kUnknown_SkColorType;
|
||||
switch (image_info.format) {
|
||||
case PixelFormat::kUnknown:
|
||||
color_type = kUnknown_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kRGBA8888:
|
||||
color_type = kRGBA_8888_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kBGRA8888:
|
||||
color_type = kBGRA_8888_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kRGBAFloat32:
|
||||
color_type = kRGBA_F32_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kGray8:
|
||||
color_type = kGray_8_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kR32Float:
|
||||
FML_DCHECK(false) << "not a supported skia format";
|
||||
break;
|
||||
}
|
||||
return SkImageInfo::Make(image_info.width, image_info.height, color_type,
|
||||
image_info.alpha_type, image_info.color_space);
|
||||
}
|
||||
|
||||
ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
|
||||
const SkImageInfo& image_info,
|
||||
const ImageInfo& image_info,
|
||||
std::optional<size_t> row_bytes)
|
||||
: buffer_(std::move(buffer)),
|
||||
generator_(nullptr),
|
||||
image_info_(image_info),
|
||||
generator_(nullptr),
|
||||
row_bytes_(row_bytes) {}
|
||||
|
||||
ImageDescriptor::ImageDescriptor(sk_sp<SkData> buffer,
|
||||
std::shared_ptr<ImageGenerator> generator)
|
||||
: buffer_(std::move(buffer)),
|
||||
image_info_(CreateImageInfo(generator->GetInfo())),
|
||||
generator_(std::move(generator)),
|
||||
image_info_(CreateImageInfo()),
|
||||
row_bytes_(std::nullopt) {}
|
||||
|
||||
Dart_Handle ImageDescriptor::initEncoded(Dart_Handle descriptor_handle,
|
||||
@ -78,31 +132,45 @@ Dart_Handle ImageDescriptor::initEncoded(Dart_Handle descriptor_handle,
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Must be kept in sync with painting.dart.
|
||||
ImageDescriptor::PixelFormat toImageDescriptorPixelFormat(int val) {
|
||||
switch (val) {
|
||||
case 0:
|
||||
return ImageDescriptor::PixelFormat::kRGBA8888;
|
||||
case 1:
|
||||
return ImageDescriptor::PixelFormat::kBGRA8888;
|
||||
case 2:
|
||||
return ImageDescriptor::PixelFormat::kRGBAFloat32;
|
||||
case 3:
|
||||
return ImageDescriptor::PixelFormat::kR32Float;
|
||||
default:
|
||||
FML_DCHECK(false) << "unrecognized format";
|
||||
return ImageDescriptor::PixelFormat::kRGBA8888;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ImageDescriptor::initRaw(Dart_Handle descriptor_handle,
|
||||
const fml::RefPtr<ImmutableBuffer>& data,
|
||||
int width,
|
||||
int height,
|
||||
int row_bytes,
|
||||
PixelFormat pixel_format) {
|
||||
SkColorType color_type = kUnknown_SkColorType;
|
||||
SkAlphaType alpha_type = kPremul_SkAlphaType;
|
||||
switch (pixel_format) {
|
||||
case PixelFormat::kRGBA8888:
|
||||
color_type = kRGBA_8888_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kBGRA8888:
|
||||
color_type = kBGRA_8888_SkColorType;
|
||||
break;
|
||||
case PixelFormat::kRGBAFloat32:
|
||||
// `PixelFormat.rgbaFloat32` is documented to not use premultiplied alpha.
|
||||
color_type = kRGBA_F32_SkColorType;
|
||||
alpha_type = kUnpremul_SkAlphaType;
|
||||
break;
|
||||
}
|
||||
FML_DCHECK(color_type != kUnknown_SkColorType);
|
||||
auto image_info = SkImageInfo::Make(width, height, color_type, alpha_type);
|
||||
int pixel_format) {
|
||||
ImageDescriptor::PixelFormat image_descriptor_pixel_format =
|
||||
toImageDescriptorPixelFormat(pixel_format);
|
||||
const ImageInfo image_info = {
|
||||
.width = static_cast<uint32_t>(width),
|
||||
.height = static_cast<uint32_t>(height),
|
||||
.format = image_descriptor_pixel_format,
|
||||
.alpha_type = image_descriptor_pixel_format == PixelFormat::kRGBAFloat32
|
||||
? kUnpremul_SkAlphaType
|
||||
: kPremul_SkAlphaType,
|
||||
.color_space = SkColorSpace::MakeSRGB(),
|
||||
};
|
||||
|
||||
auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
|
||||
data->data(), std::move(image_info),
|
||||
data->data(), image_info,
|
||||
row_bytes == -1 ? std::nullopt : std::optional<size_t>(row_bytes));
|
||||
descriptor->AssociateWithDartWrapper(descriptor_handle);
|
||||
}
|
||||
@ -115,6 +183,8 @@ ImageDecoder::TargetPixelFormat ToImageDecoderTargetPixelFormat(int32_t value) {
|
||||
return ImageDecoder::TargetPixelFormat::kDontCare;
|
||||
case 1:
|
||||
return ImageDecoder::TargetPixelFormat::kR32G32B32A32Float;
|
||||
case 2:
|
||||
return ImageDecoder::TargetPixelFormat::kR32Float;
|
||||
default:
|
||||
FML_DCHECK(false) << "Unknown pixel format.";
|
||||
return ImageDecoder::TargetPixelFormat::kUnknown;
|
||||
@ -148,4 +218,19 @@ bool ImageDescriptor::get_pixels(const SkPixmap& pixmap) const {
|
||||
pixmap.rowBytes());
|
||||
}
|
||||
|
||||
int ImageDescriptor::bytesPerPixel() const {
|
||||
switch (image_info_.format) {
|
||||
case kUnknown:
|
||||
return 0;
|
||||
case kGray8:
|
||||
return 1;
|
||||
case kRGBA8888:
|
||||
case kBGRA8888:
|
||||
case kR32Float:
|
||||
return 4;
|
||||
case kRGBAFloat32:
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "flutter/lib/ui/dart_wrapper.h"
|
||||
#include "flutter/lib/ui/painting/image_generator_registry.h"
|
||||
#include "flutter/lib/ui/painting/immutable_buffer.h"
|
||||
#include "third_party/skia/include/core/SkColorSpace.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
#include "third_party/skia/include/core/SkImage.h"
|
||||
#include "third_party/skia/include/core/SkImageInfo.h"
|
||||
@ -35,9 +36,21 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
|
||||
// This must be kept in sync with the enum in painting.dart
|
||||
enum PixelFormat {
|
||||
// Error pixel format for Skia compatibility.
|
||||
kUnknown,
|
||||
kRGBA8888,
|
||||
kBGRA8888,
|
||||
kRGBAFloat32,
|
||||
kR32Float,
|
||||
kGray8,
|
||||
};
|
||||
|
||||
struct ImageInfo {
|
||||
const uint32_t width;
|
||||
const uint32_t height;
|
||||
const PixelFormat format;
|
||||
const SkAlphaType alpha_type;
|
||||
const sk_sp<SkColorSpace> color_space;
|
||||
};
|
||||
|
||||
/// @brief Asynchronously initializes an ImageDescriptor for an encoded
|
||||
@ -57,7 +70,7 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
int width,
|
||||
int height,
|
||||
int row_bytes,
|
||||
PixelFormat pixel_format);
|
||||
int pixel_format);
|
||||
|
||||
/// @brief Associates a flutter::Codec object with the dart.ui Codec handle.
|
||||
void instantiateCodec(Dart_Handle codec,
|
||||
@ -66,19 +79,19 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
int32_t destination_format);
|
||||
|
||||
/// @brief The width of this image, EXIF oriented if applicable.
|
||||
int width() const { return image_info_.width(); }
|
||||
int width() const { return image_info_.width; }
|
||||
|
||||
/// @brief The height of this image. EXIF oriented if applicable.
|
||||
int height() const { return image_info_.height(); }
|
||||
int height() const { return image_info_.height; }
|
||||
|
||||
/// @brief The bytes per pixel of the image.
|
||||
int bytesPerPixel() const { return image_info_.bytesPerPixel(); }
|
||||
int bytesPerPixel() const;
|
||||
|
||||
/// @brief The byte length of the first row of the image.
|
||||
/// Defaults to width() * 4.
|
||||
int row_bytes() const {
|
||||
return row_bytes_.value_or(
|
||||
static_cast<size_t>(image_info_.width() * image_info_.bytesPerPixel()));
|
||||
static_cast<size_t>(image_info_.width * bytesPerPixel()));
|
||||
}
|
||||
|
||||
/// @brief Whether the given `target_width` or `target_height` differ from
|
||||
@ -97,7 +110,7 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
bool is_compressed() const { return !!generator_; }
|
||||
|
||||
/// @brief The orientation corrected image info for this image.
|
||||
const SkImageInfo& image_info() const { return image_info_; }
|
||||
const ImageInfo& image_info() const { return image_info_; }
|
||||
|
||||
/// @brief Gets the scaled dimensions of this image, if backed by an
|
||||
/// `ImageGenerator` that can perform efficient subpixel scaling.
|
||||
@ -106,7 +119,7 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
if (generator_) {
|
||||
return generator_->GetScaledDimensions(scale);
|
||||
}
|
||||
return image_info_.dimensions();
|
||||
return SkISize::Make(image_info_.width, image_info_.height);
|
||||
}
|
||||
|
||||
/// @brief Gets pixels for this image transformed based on the EXIF
|
||||
@ -119,20 +132,21 @@ class ImageDescriptor : public RefCountedDartWrappable<ImageDescriptor> {
|
||||
ClearDartWrapper();
|
||||
}
|
||||
|
||||
static ImageInfo CreateImageInfo(const SkImageInfo& sk_image_info);
|
||||
static SkImageInfo ToSkImageInfo(const ImageInfo& image_info);
|
||||
|
||||
private:
|
||||
ImageDescriptor(sk_sp<SkData> buffer,
|
||||
const SkImageInfo& image_info,
|
||||
const ImageInfo& image_info,
|
||||
std::optional<size_t> row_bytes);
|
||||
ImageDescriptor(sk_sp<SkData> buffer,
|
||||
std::shared_ptr<ImageGenerator> generator);
|
||||
|
||||
sk_sp<SkData> buffer_;
|
||||
const ImageInfo image_info_;
|
||||
std::shared_ptr<ImageGenerator> generator_;
|
||||
const SkImageInfo image_info_;
|
||||
std::optional<size_t> row_bytes_;
|
||||
|
||||
const SkImageInfo CreateImageInfo() const;
|
||||
|
||||
DEFINE_WRAPPERTYPEINFO();
|
||||
FML_FRIEND_MAKE_REF_COUNTED(ImageDescriptor);
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ImageDescriptor);
|
||||
|
||||
@ -903,6 +903,7 @@ Rasterizer::ScreenshotFormat ToScreenshotFormat(impeller::PixelFormat format) {
|
||||
case impeller::PixelFormat::kR32G32B32A32Float:
|
||||
case impeller::PixelFormat::kB10G10R10XR:
|
||||
case impeller::PixelFormat::kB10G10R10A10XR:
|
||||
case impeller::PixelFormat::kR32Float:
|
||||
FML_DCHECK(false);
|
||||
return Rasterizer::ScreenshotFormat::kUnknown;
|
||||
case impeller::PixelFormat::kR8G8B8A8UNormInt:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user