Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
image_encoding_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
5#include "png.h"
6
9
19#include "gmock/gmock.h"
20#include "gtest/gtest.h"
21
22#if IMPELLER_SUPPORTS_RENDERING
25#endif // IMPELLER_SUPPORTS_RENDERING
26
27// CREATE_NATIVE_ENTRY is leaky by design
28// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
29
30#pragma GCC diagnostic ignored "-Wunreachable-code"
31
32namespace flutter {
33namespace testing {
34
35namespace {
36fml::AutoResetWaitableEvent message_latch;
37
38class MockSyncSwitch {
39 public:
40 struct Handlers {
41 Handlers& SetIfTrue(const std::function<void()>& handler) {
43 return *this;
44 }
45 Handlers& SetIfFalse(const std::function<void()>& handler) {
47 return *this;
48 }
49 std::function<void()> true_handler = [] {};
50 std::function<void()> false_handler = [] {};
51 };
52
53 MOCK_METHOD(void, Execute, (const Handlers& handlers), (const));
54 MOCK_METHOD(void, SetSwitch, (bool value));
55};
56} // namespace
57
58TEST_F(ShellTest, EncodeImageGivesExternalTypedData) {
59 auto native_encode_image = [&](Dart_NativeArguments args) {
60 auto image_handle = Dart_GetNativeArgument(args, 0);
61 image_handle =
62 Dart_GetField(image_handle, Dart_NewStringFromCString("_image"));
63 ASSERT_FALSE(Dart_IsError(image_handle)) << Dart_GetError(image_handle);
64 ASSERT_FALSE(Dart_IsNull(image_handle));
65 auto format_handle = Dart_GetNativeArgument(args, 1);
66 auto callback_handle = Dart_GetNativeArgument(args, 2);
67
68 intptr_t peer = 0;
69 Dart_Handle result = Dart_GetNativeInstanceField(
70 image_handle, tonic::DartWrappable::kPeerIndex, &peer);
71 ASSERT_FALSE(Dart_IsError(result));
72 CanvasImage* canvas_image = reinterpret_cast<CanvasImage*>(peer);
73
74 int64_t format = -1;
75 result = Dart_IntegerToInt64(format_handle, &format);
76 ASSERT_FALSE(Dart_IsError(result));
77
78 result = EncodeImage(canvas_image, format, callback_handle);
79 ASSERT_TRUE(Dart_IsNull(result));
80 };
81
82 auto nativeValidateExternal = [&](Dart_NativeArguments args) {
83 auto handle = Dart_GetNativeArgument(args, 0);
84
85 auto typed_data_type = Dart_GetTypeOfExternalTypedData(handle);
86 EXPECT_EQ(typed_data_type, Dart_TypedData_kUint8);
87
88 message_latch.Signal();
89 };
90
91 Settings settings = CreateSettingsForFixture();
92 TaskRunners task_runners("test", // label
93 GetCurrentTaskRunner(), // platform
94 CreateNewThread(), // raster
95 CreateNewThread(), // ui
96 CreateNewThread() // io
97 );
98
99 AddNativeCallback("EncodeImage", CREATE_NATIVE_ENTRY(native_encode_image));
100 AddNativeCallback("ValidateExternal",
101 CREATE_NATIVE_ENTRY(nativeValidateExternal));
102
103 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
104
105 ASSERT_TRUE(shell->IsSetup());
106 auto configuration = RunConfiguration::InferFromSettings(settings);
107 configuration.SetEntrypoint("encodeImageProducesExternalUint8List");
108
109 shell->RunEngine(std::move(configuration), [&](auto result) {
110 ASSERT_EQ(result, Engine::RunStatus::Success);
111 });
112
113 message_latch.Wait();
114 DestroyShell(std::move(shell), task_runners);
115}
116
117TEST_F(ShellTest, EncodeImageAccessesSyncSwitch) {
118 Settings settings = CreateSettingsForFixture();
119 TaskRunners task_runners("test", // label
120 GetCurrentTaskRunner(), // platform
121 CreateNewThread(), // raster
122 CreateNewThread(), // ui
123 CreateNewThread() // io
124 );
125
126 auto native_encode_image = [&](Dart_NativeArguments args) {
127 auto image_handle = Dart_GetNativeArgument(args, 0);
128 image_handle =
129 Dart_GetField(image_handle, Dart_NewStringFromCString("_image"));
130 ASSERT_FALSE(Dart_IsError(image_handle)) << Dart_GetError(image_handle);
131 ASSERT_FALSE(Dart_IsNull(image_handle));
132 auto format_handle = Dart_GetNativeArgument(args, 1);
133
134 intptr_t peer = 0;
135 Dart_Handle result = Dart_GetNativeInstanceField(
136 image_handle, tonic::DartWrappable::kPeerIndex, &peer);
137 ASSERT_FALSE(Dart_IsError(result));
138 CanvasImage* canvas_image = reinterpret_cast<CanvasImage*>(peer);
139
140 int64_t format = -1;
141 result = Dart_IntegerToInt64(format_handle, &format);
142 ASSERT_FALSE(Dart_IsError(result));
143
144 auto io_manager = UIDartState::Current()->GetIOManager();
146
147 task_runners.GetIOTaskRunner()->PostTask([&]() {
148 auto is_gpu_disabled_sync_switch =
149 std::make_shared<const MockSyncSwitch>();
150 EXPECT_CALL(*is_gpu_disabled_sync_switch, Execute)
151 .WillOnce([](const MockSyncSwitch::Handlers& handlers) {
152 handlers.true_handler();
153 });
154 auto skia_image = canvas_image->image()
155 ? canvas_image->image()->asSkiaImage()
156 : nullptr;
158 skia_image ? skia_image->skia_image() : nullptr,
159 io_manager->GetResourceContext(), is_gpu_disabled_sync_switch);
160
161 latch.Signal();
162 });
163
164 latch.Wait();
165
166 message_latch.Signal();
167 };
168
169 AddNativeCallback("EncodeImage", CREATE_NATIVE_ENTRY(native_encode_image));
170
171 std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
172
173 ASSERT_TRUE(shell->IsSetup());
174 auto configuration = RunConfiguration::InferFromSettings(settings);
175 configuration.SetEntrypoint("encodeImageProducesExternalUint8List");
176
177 shell->RunEngine(std::move(configuration), [&](auto result) {
178 ASSERT_EQ(result, Engine::RunStatus::Success);
179 });
180
181 message_latch.Wait();
182 DestroyShell(std::move(shell), task_runners);
183}
184
185#if IMPELLER_SUPPORTS_RENDERING
186using ::impeller::testing::MockAllocator;
187using ::impeller::testing::MockBlitPass;
188using ::impeller::testing::MockCommandBuffer;
189using ::impeller::testing::MockCommandQueue;
190using ::impeller::testing::MockDeviceBuffer;
191using ::impeller::testing::MockImpellerContext;
192using ::impeller::testing::MockTexture;
193using ::testing::_;
194using ::testing::DoAll;
195using ::testing::Invoke;
196using ::testing::InvokeArgument;
197using ::testing::Return;
198
199namespace {
200std::shared_ptr<impeller::Context> MakeConvertDlImageToSkImageContext(
201 std::vector<uint8_t>& buffer) {
202 auto context = std::make_shared<MockImpellerContext>();
203 auto command_buffer = std::make_shared<MockCommandBuffer>(context);
204 auto allocator = std::make_shared<MockAllocator>();
205 auto blit_pass = std::make_shared<MockBlitPass>();
206 auto command_queue = std::make_shared<MockCommandQueue>();
207 impeller::DeviceBufferDescriptor device_buffer_desc;
208 device_buffer_desc.size = buffer.size();
209 auto device_buffer = std::make_shared<MockDeviceBuffer>(device_buffer_desc);
210 EXPECT_CALL(*allocator, OnCreateBuffer).WillOnce(Return(device_buffer));
211 EXPECT_CALL(*blit_pass, IsValid).WillRepeatedly(Return(true));
212 EXPECT_CALL(*command_buffer, IsValid).WillRepeatedly(Return(true));
213 EXPECT_CALL(*command_buffer, OnCreateBlitPass).WillOnce(Return(blit_pass));
214 EXPECT_CALL(*context, GetResourceAllocator).WillRepeatedly(Return(allocator));
215 EXPECT_CALL(*context, CreateCommandBuffer).WillOnce(Return(command_buffer));
216 EXPECT_CALL(*device_buffer, OnGetContents).WillOnce(Return(buffer.data()));
217 EXPECT_CALL(*command_queue, Submit(_, _, _))
218 .WillRepeatedly(
219 DoAll(InvokeArgument<1>(impeller::CommandBuffer::Status::kCompleted),
220 Return(fml::Status())));
221 EXPECT_CALL(*context, GetCommandQueue).WillRepeatedly(Return(command_queue));
222 return context;
223}
224} // namespace
225
226TEST_F(ShellTest, EncodeImageRetries) {
227#ifndef FML_OS_MACOSX
228 GTEST_SKIP() << "Only works on macos currently.";
229#endif
230 Settings settings = CreateSettingsForFixture();
231 settings.enable_impeller = true;
232 TaskRunners task_runners("test", // label
233 GetCurrentTaskRunner(), // platform
234 CreateNewThread(), // raster
235 CreateNewThread(), // ui
236 CreateNewThread() // io
237 );
238
239 std::unique_ptr<Shell> shell = CreateShell({
240 .settings = settings,
241 .task_runners = task_runners,
242 });
243
244 auto turn_off_gpu = [&](Dart_NativeArguments args) {
245 auto handle = Dart_GetNativeArgument(args, 0);
246 bool value = true;
247 ASSERT_TRUE(Dart_IsBoolean(handle));
248 Dart_BooleanValue(handle, &value);
249 TurnOffGPU(shell.get(), value);
250 };
251
252 AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));
253
254 auto validate_not_null = [&](Dart_NativeArguments args) {
255 auto handle = Dart_GetNativeArgument(args, 0);
256 EXPECT_FALSE(Dart_IsNull(handle));
257 message_latch.Signal();
258 };
259
260 AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null));
261
262 ASSERT_TRUE(shell->IsSetup());
263
264 auto configuration = RunConfiguration::InferFromSettings(settings);
265 configuration.SetEntrypoint("toByteDataRetries");
266
267 shell->RunEngine(std::move(configuration), [&](auto result) {
268 ASSERT_EQ(result, Engine::RunStatus::Success);
269 });
270
271 message_latch.Wait();
272 DestroyShell(std::move(shell), task_runners);
273}
274
275TEST_F(ShellTest, EncodeImageRetryOverflows) {
276#ifndef FML_OS_MACOSX
277 GTEST_SKIP() << "Only works on macos currently.";
278#endif
279 Settings settings = CreateSettingsForFixture();
280 settings.enable_impeller = true;
281 TaskRunners task_runners("test", // label
282 GetCurrentTaskRunner(), // platform
283 CreateNewThread(), // raster
284 CreateNewThread(), // ui
285 CreateNewThread() // io
286 );
287
288 std::unique_ptr<Shell> shell = CreateShell({
289 .settings = settings,
290 .task_runners = task_runners,
291 });
292
293 auto turn_off_gpu = [&](Dart_NativeArguments args) {
294 auto handle = Dart_GetNativeArgument(args, 0);
295 bool value = true;
296 ASSERT_TRUE(Dart_IsBoolean(handle));
297 Dart_BooleanValue(handle, &value);
298 TurnOffGPU(shell.get(), value);
299 };
300
301 AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));
302
303 auto validate_not_null = [&](Dart_NativeArguments args) {
304 auto handle = Dart_GetNativeArgument(args, 0);
305 EXPECT_FALSE(Dart_IsNull(handle));
306 message_latch.Signal();
307 };
308
309 AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null));
310
311 ASSERT_TRUE(shell->IsSetup());
312
313 auto configuration = RunConfiguration::InferFromSettings(settings);
314 configuration.SetEntrypoint("toByteDataRetryOverflows");
315
316 shell->RunEngine(std::move(configuration), [&](auto result) {
317 ASSERT_EQ(result, Engine::RunStatus::Success);
318 });
319
320 message_latch.Wait();
321 DestroyShell(std::move(shell), task_runners);
322}
323
324TEST_F(ShellTest, ToImageRetries) {
325#ifndef FML_OS_MACOSX
326 GTEST_SKIP() << "Only works on macos currently.";
327#endif
328 Settings settings = CreateSettingsForFixture();
329 settings.enable_impeller = true;
330 TaskRunners task_runners("test", // label
331 GetCurrentTaskRunner(), // platform
332 CreateNewThread(), // raster
333 CreateNewThread(), // ui
334 CreateNewThread() // io
335 );
336
337 std::unique_ptr<Shell> shell = CreateShell({
338 .settings = settings,
339 .task_runners = task_runners,
340 });
341
342 auto turn_off_gpu = [&](Dart_NativeArguments args) {
343 auto handle = Dart_GetNativeArgument(args, 0);
344 bool value = true;
345 ASSERT_TRUE(Dart_IsBoolean(handle));
346 Dart_BooleanValue(handle, &value);
347 TurnOffGPU(shell.get(), value);
348 };
349
350 AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));
351
352 auto validate_not_null = [&](Dart_NativeArguments args) {
353 auto handle = Dart_GetNativeArgument(args, 0);
354 EXPECT_FALSE(Dart_IsNull(handle));
355 message_latch.Signal();
356 };
357
358 AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null));
359
360 ASSERT_TRUE(shell->IsSetup());
361 auto configuration = RunConfiguration::InferFromSettings(settings);
362 configuration.SetEntrypoint("toImageRetries");
363
364 shell->RunEngine(std::move(configuration), [&](auto result) {
365 ASSERT_EQ(result, Engine::RunStatus::Success);
366 });
367
368 message_latch.Wait();
369 DestroyShell(std::move(shell), task_runners);
370}
371TEST_F(ShellTest, ToImageRetryOverflow) {
372#ifndef FML_OS_MACOSX
373 GTEST_SKIP() << "Only works on macos currently.";
374#endif
375 Settings settings = CreateSettingsForFixture();
376 settings.enable_impeller = true;
377 TaskRunners task_runners("test", // label
378 GetCurrentTaskRunner(), // platform
379 CreateNewThread(), // raster
380 CreateNewThread(), // ui
381 CreateNewThread() // io
382 );
383
384 std::unique_ptr<Shell> shell = CreateShell({
385 .settings = settings,
386 .task_runners = task_runners,
387 });
388
389 auto turn_off_gpu = [&](Dart_NativeArguments args) {
390 auto handle = Dart_GetNativeArgument(args, 0);
391 bool value = true;
392 ASSERT_TRUE(Dart_IsBoolean(handle));
393 Dart_BooleanValue(handle, &value);
394 TurnOffGPU(shell.get(), value);
395 };
396
397 AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));
398
399 auto validate_not_null = [&](Dart_NativeArguments args) {
400 auto handle = Dart_GetNativeArgument(args, 0);
401 EXPECT_FALSE(Dart_IsNull(handle));
402 message_latch.Signal();
403 };
404
405 AddNativeCallback("ValidateNotNull", CREATE_NATIVE_ENTRY(validate_not_null));
406
407 ASSERT_TRUE(shell->IsSetup());
408 auto configuration = RunConfiguration::InferFromSettings(settings);
409 configuration.SetEntrypoint("toImageRetryOverflows");
410
411 shell->RunEngine(std::move(configuration), [&](auto result) {
412 ASSERT_EQ(result, Engine::RunStatus::Success);
413 });
414
415 message_latch.Wait();
416 DestroyShell(std::move(shell), task_runners);
417}
418
419TEST_F(ShellTest, EncodeImageFailsWithoutGPUImpeller) {
420#ifndef FML_OS_MACOSX
421 // Only works on macos currently.
422 GTEST_SKIP();
423#endif
424 Settings settings = CreateSettingsForFixture();
425 settings.enable_impeller = true;
426 TaskRunners task_runners("test", // label
427 GetCurrentTaskRunner(), // platform
428 CreateNewThread(), // raster
429 CreateNewThread(), // ui
430 CreateNewThread() // io
431 );
432
433 auto native_validate_error = [&](Dart_NativeArguments args) {
434 auto handle = Dart_GetNativeArgument(args, 0);
435
436 EXPECT_FALSE(Dart_IsNull(handle));
437
438 message_latch.Signal();
439 };
440
441 AddNativeCallback("ValidateError",
442 CREATE_NATIVE_ENTRY(native_validate_error));
443
444 std::unique_ptr<Shell> shell = CreateShell({
445 .settings = settings,
446 .task_runners = task_runners,
447 });
448
449 auto turn_off_gpu = [&](Dart_NativeArguments args) {
450 auto handle = Dart_GetNativeArgument(args, 0);
451 bool value = true;
452 ASSERT_TRUE(Dart_IsBoolean(handle));
453 Dart_BooleanValue(handle, &value);
454 TurnOffGPU(shell.get(), true);
455 };
456
457 AddNativeCallback("TurnOffGPU", CREATE_NATIVE_ENTRY(turn_off_gpu));
458
459 auto flush_awaiting_tasks = [&](Dart_NativeArguments args) {
460 fml::WeakPtr io_manager = shell->GetIOManager();
461 task_runners.GetIOTaskRunner()->PostTask([io_manager] {
462 if (io_manager) {
463 std::shared_ptr<impeller::Context> impeller_context =
464 io_manager->GetImpellerContext();
465 // This will cause the stored tasks to overflow and start throwing them
466 // away.
467 for (int i = 0; i < impeller::Context::kMaxTasksAwaitingGPU; i++) {
468 impeller_context->StoreTaskForGPU([] {}, [] {});
469 }
470 }
471 });
472 };
473
474 AddNativeCallback("FlushGpuAwaitingTasks",
475 CREATE_NATIVE_ENTRY(flush_awaiting_tasks));
476
477 ASSERT_TRUE(shell->IsSetup());
478 auto configuration = RunConfiguration::InferFromSettings(settings);
479 configuration.SetEntrypoint("toByteDataWithoutGPU");
480
481 shell->RunEngine(std::move(configuration), [&](auto result) {
482 ASSERT_EQ(result, Engine::RunStatus::Success);
483 });
484
485 message_latch.Wait();
486 DestroyShell(std::move(shell), task_runners);
487}
488
489TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage16Float) {
490 sk_sp<MockDlImage> image(new MockDlImage());
491 EXPECT_CALL(*image, GetSize) //
492 .WillRepeatedly(Return(DlISize(100, 100)));
493
496 auto texture = std::make_shared<MockTexture>(desc);
497 EXPECT_CALL(*texture, GetSize)
498 .WillRepeatedly(Return(impeller::ISize(100, 100)));
499 EXPECT_CALL(*image, GetImpellerTexture(::testing::_))
500 .WillOnce(Return(texture));
501 std::vector<uint8_t> buffer;
502 buffer.reserve(100 * 100 * 8);
503 auto context = MakeConvertDlImageToSkImageContext(buffer);
504 bool did_call = false;
505 MockSnapshotDelegate snapshot_delegate;
506 EXPECT_CALL(snapshot_delegate, MakeRenderContextCurrent)
507 .WillRepeatedly(Return(true));
509 image,
510 [&did_call](const fml::StatusOr<sk_sp<SkImage>>& image) {
511 did_call = true;
512 ASSERT_TRUE(image.ok());
513 ASSERT_TRUE(image.value());
514 EXPECT_EQ(100, image.value()->width());
515 EXPECT_EQ(100, image.value()->height());
516 EXPECT_EQ(kRGBA_F16_SkColorType, image.value()->colorType());
517 EXPECT_EQ(nullptr, image.value()->colorSpace());
518 },
519 snapshot_delegate.GetWeakPtr(), context);
520 EXPECT_TRUE(did_call);
521}
522
523TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImage10XR) {
524 sk_sp<MockDlImage> image(new MockDlImage());
525 EXPECT_CALL(*image, GetSize) //
526 .WillRepeatedly(Return(DlISize(100, 100)));
527
530 auto texture = std::make_shared<MockTexture>(desc);
531 EXPECT_CALL(*texture, GetSize)
532 .WillRepeatedly(Return(impeller::ISize(100, 100)));
533 EXPECT_CALL(*image, GetImpellerTexture(::testing::_))
534 .WillOnce(Return(texture));
535 std::vector<uint8_t> buffer;
536 buffer.reserve(100 * 100 * 4);
537 auto context = MakeConvertDlImageToSkImageContext(buffer);
538 bool did_call = false;
539 MockSnapshotDelegate snapshot_delegate;
540 EXPECT_CALL(snapshot_delegate, MakeRenderContextCurrent)
541 .WillRepeatedly(Return(true));
543 image,
544 [&did_call](const fml::StatusOr<sk_sp<SkImage>>& image) {
545 did_call = true;
546 ASSERT_TRUE(image.ok());
547 ASSERT_TRUE(image.value());
548 EXPECT_EQ(100, image.value()->width());
549 EXPECT_EQ(100, image.value()->height());
550 EXPECT_EQ(kBGR_101010x_XR_SkColorType, image.value()->colorType());
551 EXPECT_EQ(nullptr, image.value()->colorSpace());
552 },
553 snapshot_delegate.GetWeakPtr(), context);
554 EXPECT_TRUE(did_call);
555}
556
557TEST(ImageEncodingImpellerTest, ConvertDlImageToSkImageTextureSizeMismatch) {
558 DlISize dl_image_size(100, 100);
559 sk_sp<MockDlImage> image(new MockDlImage());
560 EXPECT_CALL(*image, GetSize) //
561 .WillRepeatedly(Return(dl_image_size));
562
563 // Create a texture that is smaller than the DlImage.
566 desc.size = impeller::ISize(50, 50);
567 auto texture = std::make_shared<MockTexture>(desc);
568 EXPECT_CALL(*texture, GetSize).WillRepeatedly(Return(desc.size));
569 EXPECT_CALL(*image, GetImpellerTexture(::testing::_))
570 .WillOnce(Return(texture));
571 std::vector<uint8_t> buffer;
572 buffer.reserve(desc.size.width * desc.size.height *
574 auto context = MakeConvertDlImageToSkImageContext(buffer);
575
576 bool did_call = false;
577 MockSnapshotDelegate snapshot_delegate;
578 EXPECT_CALL(snapshot_delegate, MakeRenderContextCurrent)
579 .WillRepeatedly(Return(true));
581 image,
582 [&](const fml::StatusOr<sk_sp<SkImage>>& result) {
583 did_call = true;
584 ASSERT_TRUE(result.ok());
585 ASSERT_TRUE(result.value());
586 // The SkImage size should match the size of the actual texture, not
587 // the size of the DlImage.
588 EXPECT_EQ(desc.size.width, result.value()->width());
589 EXPECT_EQ(desc.size.height, result.value()->height());
590 },
591 snapshot_delegate.GetWeakPtr(), context);
592 EXPECT_TRUE(did_call);
593}
594
595TEST(ImageEncodingImpellerTest, PngEncoding10XR) {
596 int width = 100;
597 int height = 100;
598 SkImageInfo info = SkImageInfo::Make(
599 width, height, kBGR_101010x_XR_SkColorType, kUnpremul_SkAlphaType);
600
601 auto surface = SkSurfaces::Raster(info);
602 SkCanvas* canvas = surface->getCanvas();
603
604 SkPaint paint;
605 paint.setColor(SK_ColorBLUE);
606 paint.setAntiAlias(true);
607
608 canvas->clear(SK_ColorWHITE);
609 canvas->drawCircle(width / 2, height / 2, 100, paint);
610
611 sk_sp<SkImage> image = surface->makeImageSnapshot();
612
614 EXPECT_TRUE(png.ok());
615}
616
617namespace {
618struct PngMemoryReader {
619 const uint8_t* data;
620 size_t offset;
621 size_t size;
622};
623
624void PngMemoryRead(png_structp png_ptr,
625 png_bytep out_bytes,
626 png_size_t byte_count_to_read) {
627 PngMemoryReader* memory_reader =
628 reinterpret_cast<PngMemoryReader*>(png_get_io_ptr(png_ptr));
629 if (memory_reader->offset + byte_count_to_read > memory_reader->size) {
630 png_error(png_ptr, "Read error in PngMemoryRead");
631 }
632 memcpy(out_bytes, memory_reader->data + memory_reader->offset,
633 byte_count_to_read);
634 memory_reader->offset += byte_count_to_read;
635}
636
637fml::StatusOr<std::vector<uint32_t>> ReadPngFromMemory(const uint8_t* png_data,
638 size_t png_size) {
639 png_structp png =
640 png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
641 if (!png) {
642 return fml::Status(fml::StatusCode::kAborted, "unknown");
643 }
644
645 png_infop info = png_create_info_struct(png);
646 if (!info) {
647 png_destroy_read_struct(&png, nullptr, nullptr);
648 return fml::Status(fml::StatusCode::kAborted, "unknown");
649 }
650
651 fml::ScopedCleanupClosure png_cleanup(
652 [&png, &info]() { png_destroy_read_struct(&png, &info, nullptr); });
653
654 if (setjmp(png_jmpbuf(png))) {
655 return fml::Status(fml::StatusCode::kAborted, "unknown");
656 }
657
658 PngMemoryReader memory_reader = {
659 .data = png_data, .offset = 0, .size = png_size};
660 png_set_read_fn(png, &memory_reader, PngMemoryRead);
661
662 png_read_info(png, info);
663
664 int width = png_get_image_width(png, info);
665 int height = png_get_image_height(png, info);
666 png_byte color_type = png_get_color_type(png, info);
667 png_byte bit_depth = png_get_bit_depth(png, info);
668
669 if (bit_depth == 16) {
670 png_set_strip_16(png);
671 }
672 if (color_type == PNG_COLOR_TYPE_PALETTE) {
673 png_set_palette_to_rgb(png);
674 }
675
676 png_read_update_info(png, info);
677 std::vector<uint32_t> result(width * height);
678 std::vector<png_bytep> row_pointers;
679 row_pointers.reserve(height);
680
681 for (int i = 0; i < height; ++i) {
682 row_pointers.push_back(
683 reinterpret_cast<png_bytep>(result.data() + i * width));
684 }
685
686 png_read_image(png, row_pointers.data());
687
688 return result;
689}
690} // namespace
691
692TEST(ImageEncodingImpellerTest, PngEncodingBGRA10XR) {
693 int width = 100;
694 int height = 100;
695 SkImageInfo info = SkImageInfo::Make(
696 width, height, kBGRA_10101010_XR_SkColorType, kUnpremul_SkAlphaType);
697
698 auto surface = SkSurfaces::Raster(info);
699 SkCanvas* canvas = surface->getCanvas();
700
701 SkPaint paint;
702 paint.setColor(SK_ColorBLUE);
703 paint.setAntiAlias(true);
704
705 canvas->clear(SK_ColorRED);
706 canvas->drawCircle(width / 2, height / 2, 25, paint);
707
708 sk_sp<SkImage> image = surface->makeImageSnapshot();
709
711 ASSERT_TRUE(png.ok());
713 ReadPngFromMemory(png.value()->bytes(), png.value()->size());
714 ASSERT_TRUE(pixels.ok());
715 EXPECT_EQ(pixels.value()[0], 0xff0000ff);
716 int middle = 100 * 50 + 50;
717 EXPECT_EQ(pixels.value()[middle], 0xffff0000);
718}
719
720#endif // IMPELLER_SUPPORTS_RENDERING
721
722} // namespace testing
723} // namespace flutter
724
725// NOLINTEND(clang-analyzer-core.StackAddressEscape)
sk_sp< DlImage > image() const
Definition image.h:56
static void ConvertDlImageToSkImage(const sk_sp< DlImage > &dl_image, std::function< void(fml::StatusOr< sk_sp< SkImage > >)> encode_task, const fml::TaskRunnerAffineWeakPtr< SnapshotDelegate > &snapshot_delegate, const std::shared_ptr< impeller::Context > &impeller_context)
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
fml::RefPtr< fml::TaskRunner > GetIOTaskRunner() const
fml::WeakPtr< IOManager > GetIOManager() const
static UIDartState * Current()
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
const T & value() const
Definition status_or.h:77
bool ok() const
Definition status_or.h:75
virtual void PostTask(const fml::closure &task) override
static constexpr int32_t kMaxTasksAwaitingGPU
Definition context.h:84
int32_t value
FlutterVulkanImage * image
VkSurfaceKHR surface
Definition main.cc:65
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar FlBinaryMessengerMessageHandler handler
uint32_t uint32_t * format
std::shared_ptr< ImpellerAllocator > allocator
std::function< void()> true_handler
std::function< void()> false_handler
FlTexture * texture
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
fml::RefPtr< fml::TaskRunner > GetCurrentTaskRunner()
TEST_F(DisplayListTest, Defaults)
TEST(NativeAssetsManagerTest, NoAvailableAssets)
impeller::ISize32 DlISize
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
sk_sp< SkImage > ConvertToRasterUsingResourceContext(const sk_sp< SkImage > &image, const fml::WeakPtr< GrDirectContext > &resource_context, const std::shared_ptr< const SyncSwitch > &is_gpu_disabled_sync_switch)
Dart_Handle EncodeImage(CanvasImage *canvas_image, int format, Dart_Handle callback_handle)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Definition formats.h:643
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
ISize64 ISize
Definition size.h:162
std::shared_ptr< ContextGLES > context
std::shared_ptr< CommandBuffer > command_buffer
uint32_t color_type
int32_t height
int32_t width
Type height
Definition size.h:29
Type width
Definition size.h:28
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define CREATE_NATIVE_ENTRY(native_entry)