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 "flutter/lib/ui/painting/immutable_buffer.h" |
6 | |
7 | #include "flutter/lib/ui/ui_dart_state.h" |
8 | #include "third_party/tonic/converter/dart_converter.h" |
9 | #include "third_party/tonic/dart_args.h" |
10 | #include "third_party/tonic/dart_binding_macros.h" |
11 | |
12 | #if OS_ANDROID |
13 | #include <sys/mman.h> |
14 | #endif |
15 | |
16 | namespace flutter { |
17 | |
18 | IMPLEMENT_WRAPPERTYPEINFO(ui, ImmutableBuffer); |
19 | |
20 | #define FOR_EACH_BINDING(V) \ |
21 | V(ImmutableBuffer, dispose) \ |
22 | V(ImmutableBuffer, length) |
23 | |
24 | FOR_EACH_BINDING(DART_NATIVE_CALLBACK) |
25 | |
26 | ImmutableBuffer::~ImmutableBuffer() {} |
27 | |
28 | void ImmutableBuffer::RegisterNatives(tonic::DartLibraryNatives* natives) { |
29 | natives->Register({{"ImmutableBuffer_init" , ImmutableBuffer::init, 3, true}, |
30 | FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); |
31 | } |
32 | |
33 | void ImmutableBuffer::init(Dart_NativeArguments args) { |
34 | Dart_Handle callback_handle = Dart_GetNativeArgument(args, 2); |
35 | if (!Dart_IsClosure(callback_handle)) { |
36 | Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function" )); |
37 | return; |
38 | } |
39 | |
40 | Dart_Handle buffer_handle = Dart_GetNativeArgument(args, 0); |
41 | tonic::Uint8List data = tonic::Uint8List(Dart_GetNativeArgument(args, 1)); |
42 | |
43 | auto sk_data = MakeSkDataWithCopy(data.data(), data.num_elements()); |
44 | data.Release(); |
45 | auto buffer = fml::MakeRefCounted<ImmutableBuffer>(sk_data); |
46 | buffer->AssociateWithDartWrapper(buffer_handle); |
47 | tonic::DartInvoke(callback_handle, {Dart_TypeVoid()}); |
48 | } |
49 | |
50 | size_t ImmutableBuffer::GetAllocationSize() const { |
51 | return sizeof(ImmutableBuffer) + data_->size(); |
52 | } |
53 | |
54 | #if OS_ANDROID |
55 | |
56 | // Compressed image buffers are allocated on the UI thread but are deleted on a |
57 | // decoder worker thread. Android's implementation of malloc appears to |
58 | // continue growing the native heap size when the allocating thread is |
59 | // different from the freeing thread. To work around this, create an SkData |
60 | // backed by an anonymous mapping. |
61 | sk_sp<SkData> ImmutableBuffer::MakeSkDataWithCopy(const void* data, |
62 | size_t length) { |
63 | if (length == 0) { |
64 | return SkData::MakeEmpty(); |
65 | } |
66 | |
67 | size_t mapping_length = length + sizeof(size_t); |
68 | void* mapping = ::mmap(nullptr, mapping_length, PROT_READ | PROT_WRITE, |
69 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
70 | |
71 | if (mapping == MAP_FAILED) { |
72 | return SkData::MakeEmpty(); |
73 | } |
74 | |
75 | *reinterpret_cast<size_t*>(mapping) = mapping_length; |
76 | void* mapping_data = reinterpret_cast<char*>(mapping) + sizeof(size_t); |
77 | ::memcpy(mapping_data, data, length); |
78 | |
79 | SkData::ReleaseProc proc = [](const void* ptr, void* context) { |
80 | size_t* size_ptr = reinterpret_cast<size_t*>(context); |
81 | FML_DCHECK(ptr == size_ptr + 1); |
82 | if (::munmap(const_cast<void*>(context), *size_ptr) == -1) { |
83 | FML_LOG(ERROR) << "munmap of codec SkData failed" ; |
84 | } |
85 | }; |
86 | |
87 | return SkData::MakeWithProc(mapping_data, length, proc, mapping); |
88 | } |
89 | |
90 | #else |
91 | |
92 | sk_sp<SkData> ImmutableBuffer::MakeSkDataWithCopy(const void* data, |
93 | size_t length) { |
94 | return SkData::MakeWithCopy(data, length); |
95 | } |
96 | |
97 | #endif // OS_ANDROID |
98 | |
99 | } // namespace flutter |
100 | |