Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
image_decoder_no_gl_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6#include <memory>
7
10#include "include/core/SkColorType.h"
11#include "third_party/skia/include/codec/SkPngDecoder.h"
12
13namespace flutter {
14namespace testing {
15
16// Tests are disabled for fuchsia.
17#if defined(OS_FUCHSIA)
18#pragma GCC diagnostic ignored "-Wunreachable-code"
19#endif
20
21namespace {
22
23bool IsPngWithPLTE(const uint8_t* bytes, size_t size) {
24 constexpr std::string_view kPngMagic = "\x89PNG\x0d\x0a\x1a\x0a";
25 constexpr std::string_view kPngPlte = "PLTE";
26 constexpr uint32_t kLengthBytes = 4;
27 constexpr uint32_t kTypeBytes = 4;
28 constexpr uint32_t kCrcBytes = 4;
29
30 if (size < kPngMagic.size()) {
31 return false;
32 }
33
34 if (memcmp(bytes, kPngMagic.data(), kPngMagic.size()) != 0) {
35 return false;
36 }
37
38 const uint8_t* end = bytes + size;
39 const uint8_t* loc = bytes + kPngMagic.size();
40 while (loc + kLengthBytes + kTypeBytes <= end) {
41 uint32_t chunk_length =
42 fml::BigEndianToArch(*reinterpret_cast<const uint32_t*>(loc));
43
44 if (memcmp(loc + kLengthBytes, kPngPlte.data(), kPngPlte.size()) == 0) {
45 return true;
46 }
47
48 loc += kLengthBytes + kTypeBytes + chunk_length + kCrcBytes;
49 }
50
51 return false;
52}
53
54} // namespace
55
56float HalfToFloat(uint16_t half) {
57 switch (half) {
58 case 0x7c00:
59 return std::numeric_limits<float>::infinity();
60 case 0xfc00:
61 return -std::numeric_limits<float>::infinity();
62 }
63 bool negative = half >> 15;
64 uint16_t exponent = (half >> 10) & 0x1f;
65 uint16_t fraction = half & 0x3ff;
66 float fExponent = exponent - 15.0f;
67 float fFraction = static_cast<float>(fraction) / 1024.f;
68 float pow_value = powf(2.0f, fExponent);
69 return (negative ? -1.f : 1.f) * pow_value * (1.0f + fFraction);
70}
71
72float DecodeBGR10(uint32_t x) {
73 const float max = 1.25098f;
74 const float min = -0.752941f;
75 const float intercept = min;
76 const float slope = (max - min) / 1024.0f;
77 return (x * slope) + intercept;
78}
79
80TEST(ImageDecoderNoGLTest, ImpellerWideGamutDisplayP3) {
81#if defined(OS_FUCHSIA)
82 GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
83#endif
84 SkCodecs::Register(SkPngDecoder::Decoder());
85 auto data = flutter::testing::OpenFixtureAsSkData("DisplayP3Logo.png");
86 auto image = SkImages::DeferredFromEncodedData(data);
87 std::shared_ptr<impeller::Capabilities> capabilities =
90 .Build();
91 ASSERT_TRUE(image != nullptr);
92 ASSERT_EQ(SkISize::Make(100, 100), image->dimensions());
93
95 std::shared_ptr<ImageGenerator> generator =
97 ASSERT_TRUE(generator);
98
99 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
100 std::move(generator));
101
102 ASSERT_FALSE(
103 IsPngWithPLTE(descriptor->data()->bytes(), descriptor->data()->size()));
104
105#if IMPELLER_SUPPORTS_RENDERING
106 std::shared_ptr<impeller::Allocator> allocator =
107 std::make_shared<impeller::TestImpellerAllocator>();
108 absl::StatusOr<ImageDecoderImpeller::DecompressResult> wide_result =
110 descriptor.get(), {.target_width = 100, .target_height = 100},
111 {100, 100},
112 /*supports_wide_gamut=*/true, capabilities, allocator);
113 ASSERT_TRUE(wide_result.ok());
114 ASSERT_EQ(wide_result->image_info.format,
116
117 const uint16_t* half_ptr = reinterpret_cast<const uint16_t*>(
118 wide_result->device_buffer->OnGetContents());
119 bool found_deep_red = false;
120 for (int i = 0; i < wide_result->image_info.size.width *
121 wide_result->image_info.size.height;
122 ++i) {
123 float red = HalfToFloat(*half_ptr++);
124 float green = HalfToFloat(*half_ptr++);
125 float blue = HalfToFloat(*half_ptr++);
126 half_ptr++; // alpha
127 if (fabsf(red - 1.0931f) < 0.01f && fabsf(green - -0.2268f) < 0.01f &&
128 fabsf(blue - -0.1501f) < 0.01f) {
129 found_deep_red = true;
130 break;
131 }
132 }
133
134 ASSERT_TRUE(found_deep_red);
135 absl::StatusOr<ImageDecoderImpeller::DecompressResult> narrow_result =
137 descriptor.get(), {.target_width = 100, .target_height = 100},
138 {100, 100},
139 /*supports_wide_gamut=*/false, capabilities, allocator);
140
141 ASSERT_TRUE(narrow_result.ok());
142 ASSERT_EQ(narrow_result->image_info.format,
144#endif // IMPELLER_SUPPORTS_RENDERING
145}
146
147TEST(ImageDecoderNoGLTest, ImpellerWideGamutIndexedPng) {
148#if defined(OS_FUCHSIA)
149 GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
150#endif
151 SkCodecs::Register(SkPngDecoder::Decoder());
152 auto data = flutter::testing::OpenFixtureAsSkData("WideGamutIndexed.png");
153 auto image = SkImages::DeferredFromEncodedData(data);
154 std::shared_ptr<impeller::Capabilities> capabilities =
157 .Build();
158 ASSERT_TRUE(image != nullptr);
159 ASSERT_EQ(SkISize::Make(100, 100), image->dimensions());
160
161 ImageGeneratorRegistry registry;
162 std::shared_ptr<ImageGenerator> generator =
164 ASSERT_TRUE(generator);
165
166 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
167 std::move(generator));
168
169 ASSERT_TRUE(
170 IsPngWithPLTE(descriptor->data()->bytes(), descriptor->data()->size()));
171
172#if IMPELLER_SUPPORTS_RENDERING
173 std::shared_ptr<impeller::Allocator> allocator =
174 std::make_shared<impeller::TestImpellerAllocator>();
175 absl::StatusOr<ImageDecoderImpeller::DecompressResult> wide_result =
177 descriptor.get(), {.target_width = 100, .target_height = 100},
178 {100, 100},
179 /*supports_wide_gamut=*/true, capabilities, allocator);
180 ASSERT_TRUE(wide_result.ok());
181 ASSERT_EQ(wide_result->image_info.format,
183
184 const uint32_t* pixel_ptr = reinterpret_cast<const uint32_t*>(
185 wide_result->device_buffer->OnGetContents());
186 bool found_deep_red = false;
187 for (int i = 0; i < wide_result->image_info.size.width *
188 wide_result->image_info.size.height;
189 ++i) {
190 uint32_t pixel = *pixel_ptr++;
191 float blue = DecodeBGR10((pixel >> 0) & 0x3ff);
192 float green = DecodeBGR10((pixel >> 10) & 0x3ff);
193 float red = DecodeBGR10((pixel >> 20) & 0x3ff);
194 if (fabsf(red - 1.0931f) < 0.01f && fabsf(green - -0.2268f) < 0.01f &&
195 fabsf(blue - -0.1501f) < 0.01f) {
196 found_deep_red = true;
197 break;
198 }
199 }
200
201 ASSERT_TRUE(found_deep_red);
202 absl::StatusOr<ImageDecoderImpeller::DecompressResult> narrow_result =
204 descriptor.get(), {.target_width = 100, .target_height = 100},
205 {100, 100},
206 /*supports_wide_gamut=*/false, capabilities, allocator);
207
208 ASSERT_TRUE(narrow_result.ok());
209 ASSERT_EQ(narrow_result->image_info.format,
211#endif // IMPELLER_SUPPORTS_RENDERING
212}
213
214TEST(ImageDecoderNoGLTest, ImpellerRGBA32FDecode) {
215#if defined(OS_FUCHSIA)
216 GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
217#endif
218
219#if IMPELLER_SUPPORTS_RENDERING
220 // 1. Create a 1x1 pixel with float RGBA values.
221 float pixel_data[] = {1.0f, 0.5f, 0.25f, 1.0f}; // R, G, B, A
222 sk_sp<SkData> sk_data = SkData::MakeWithCopy(pixel_data, sizeof(pixel_data));
223 auto immutable_buffer =
224 fml::MakeRefCounted<ImmutableBuffer>(std::move(sk_data));
225
226 // 2. Create an ImageDescriptor using the private constructor.
227 ImageDescriptor::ImageInfo image_info = {
228 .width = 1,
229 .height = 1,
231 .alpha_type = kUnpremul_SkAlphaType,
232 };
233 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
234 immutable_buffer->data(), image_info, sizeof(pixel_data));
235
236 // Set up Impeller capabilities and allocator.
237 std::shared_ptr<impeller::Capabilities> capabilities =
240 .Build();
241 std::shared_ptr<impeller::Allocator> allocator =
242 std::make_shared<impeller::TestImpellerAllocator>();
243
244 // 3. Call ImageDecoderImpeller::DecompressTexture with this ImageDescriptor.
245 absl::StatusOr<ImageDecoderImpeller::DecompressResult> result =
247 descriptor.get(),
248 /*options=*/
249 {.target_width = 1,
250 .target_height = 1,
251 .target_format =
252 ImageDecoder::TargetPixelFormat::kR32G32B32A32Float},
253 /*max_texture_size=*/{1, 1},
254 /*supports_wide_gamut=*/true, capabilities, allocator);
255
256 // 4. Assert that wide_result->image_info.format is
257 // impeller::PixelFormat::kR32G32B32A32Float.
258 ASSERT_TRUE(result.ok());
259 ASSERT_EQ(result->image_info.format,
261
262 // Optionally, verify the pixel data if needed.
263 const float* decompressed_pixel_ptr =
264 reinterpret_cast<const float*>(result->device_buffer->OnGetContents());
265 ASSERT_NE(decompressed_pixel_ptr, nullptr);
266 EXPECT_EQ(decompressed_pixel_ptr[0], 1.0f); // R
267 EXPECT_EQ(decompressed_pixel_ptr[1], 0.5f); // G
268 EXPECT_EQ(decompressed_pixel_ptr[2], 0.25f); // B
269 EXPECT_EQ(decompressed_pixel_ptr[3], 1.0f); // A
270
271#endif // IMPELLER_SUPPORTS_RENDERING
272}
273
274TEST(ImageDecoderNoGLTest, ImpellerR32FDecode) {
275#if defined(OS_FUCHSIA)
276 GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
277#endif
278
279#if !IMPELLER_SUPPORTS_RENDERING
280 GTEST_SKIP() << "test only supported on impeller";
281#else
282 // 1. Create a 1x1 pixel with float RGBA values.
283 float pixel_data[] = {1.0f};
284 sk_sp<SkData> sk_data = SkData::MakeWithCopy(pixel_data, sizeof(pixel_data));
285 auto immutable_buffer =
286 fml::MakeRefCounted<ImmutableBuffer>(std::move(sk_data));
287
288 // 2. Create an ImageDescriptor using the private constructor.
289 ImageDescriptor::ImageInfo image_info = {
290 .width = 1,
291 .height = 1,
293 .alpha_type = kUnpremul_SkAlphaType,
294 };
295 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(
296 immutable_buffer->data(), image_info, sizeof(pixel_data));
297
298 // Set up Impeller capabilities and allocator.
299 std::shared_ptr<impeller::Capabilities> capabilities =
302 .Build();
303 std::shared_ptr<impeller::Allocator> allocator =
304 std::make_shared<impeller::TestImpellerAllocator>();
305
306 // 3. Call ImageDecoderImpeller::DecompressTexture with this ImageDescriptor.
307 absl::StatusOr<ImageDecoderImpeller::DecompressResult> result =
309 descriptor.get(),
310 /*options=*/
311 {.target_width = 1,
312 .target_height = 1,
313 .target_format = ImageDecoder::TargetPixelFormat::kR32Float},
314 /*max_texture_size=*/{1, 1},
315 /*supports_wide_gamut=*/true, capabilities, allocator);
316
317 // 4. Assert that wide_result->image_info.format is
318 // impeller::PixelFormat::kR32G32B32A32Float.
319 ASSERT_TRUE(result.ok());
320 ASSERT_EQ(result->image_info.format, impeller::PixelFormat::kR32Float);
321
322 // Optionally, verify the pixel data if needed.
323 const float* decompressed_pixel_ptr =
324 reinterpret_cast<const float*>(result->device_buffer->OnGetContents());
325 ASSERT_NE(decompressed_pixel_ptr, nullptr);
326 EXPECT_EQ(decompressed_pixel_ptr[0], 1.0f);
327
328#endif // IMPELLER_SUPPORTS_RENDERING
329}
330
331TEST(ImageDecoderNoGLTest, ImpellerUnmultipliedAlphaPng) {
332#if defined(OS_FUCHSIA)
333 GTEST_SKIP() << "Fuchsia can't load the test fixtures.";
334#endif
335 SkCodecs::Register(SkPngDecoder::Decoder());
336 auto data = flutter::testing::OpenFixtureAsSkData("unmultiplied_alpha.png");
337 auto image = SkImages::DeferredFromEncodedData(data);
338 std::shared_ptr<impeller::Capabilities> capabilities =
341 .Build();
342 ASSERT_TRUE(image != nullptr);
343 ASSERT_EQ(SkISize::Make(11, 11), image->dimensions());
344
345 ImageGeneratorRegistry registry;
346 std::shared_ptr<ImageGenerator> generator =
348 ASSERT_TRUE(generator);
349
350 auto descriptor = fml::MakeRefCounted<ImageDescriptor>(std::move(data),
351 std::move(generator));
352
353#if IMPELLER_SUPPORTS_RENDERING
354 std::shared_ptr<impeller::Allocator> allocator =
355 std::make_shared<impeller::TestImpellerAllocator>();
356 absl::StatusOr<ImageDecoderImpeller::DecompressResult> result =
358 descriptor.get(), {.target_width = 11, .target_height = 11}, {11, 11},
359 /*supports_wide_gamut=*/true, capabilities, allocator);
360 ASSERT_TRUE(result.ok());
361 ASSERT_EQ(result->image_info.format,
363
364 const uint32_t* pixel_ptr =
365 reinterpret_cast<const uint32_t*>(result->device_buffer->OnGetContents());
366 // Test the upper left pixel is premultiplied and not solid red.
367 ASSERT_EQ(*pixel_ptr, (uint32_t)0x1000001);
368 // Test a pixel in the green box is still green.
369 ASSERT_EQ(*(pixel_ptr + 11 * 4 + 4), (uint32_t)0xFF00FF00);
370
371#endif // IMPELLER_SUPPORTS_RENDERING
372}
373
374} // namespace testing
375} // namespace flutter
static absl::StatusOr< DecompressResult > DecompressTexture(ImageDescriptor *descriptor, const ImageDecoder::Options &options, impeller::ISize max_texture_size, bool supports_wide_gamut, const std::shared_ptr< const impeller::Capabilities > &capabilities, const std::shared_ptr< impeller::Allocator > &allocator)
Keeps a priority-ordered registry of image generator builders to be used when decoding images....
std::shared_ptr< ImageGenerator > CreateCompatibleGenerator(const sk_sp< SkData > &buffer)
Walks the list of image generator builders in descending priority order until a compatible ImageGener...
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
std::unique_ptr< Capabilities > Build()
int32_t x
FlutterVulkanImage * image
std::shared_ptr< ImpellerAllocator > allocator
sk_sp< SkData > OpenFixtureAsSkData(const std::string &fixture_name)
Opens a fixture of the given file name and returns a Skia SkData holding its contents.
Definition testing.cc:63
TEST(NativeAssetsManagerTest, NoAvailableAssets)
float HalfToFloat(uint16_t half)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
constexpr T BigEndianToArch(T n)
Convert a known big endian value to match the endianness of the current architecture....
Definition endianness.h:59
const size_t end