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