1 // Copyright 2014 The Chromium 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 "ui/android/resources/resource_manager_impl.h"
6
7 #include <inttypes.h>
8 #include <stddef.h>
9
10 #include <utility>
11 #include <vector>
12
13 #include "base/android/jni_array.h"
14 #include "base/android/jni_string.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/trace_event/memory_dump_manager.h"
19 #include "base/trace_event/memory_usage_estimator.h"
20 #include "base/trace_event/process_memory_dump.h"
21 #include "base/trace_event/trace_event.h"
22 #include "cc/resources/scoped_ui_resource.h"
23 #include "cc/resources/ui_resource_manager.h"
24 #include "third_party/skia/include/core/SkBitmap.h"
25 #include "third_party/skia/include/core/SkCanvas.h"
26 #include "third_party/skia/include/core/SkColorFilter.h"
27 #include "ui/android/resources/ui_resource_provider.h"
28 #include "ui/android/ui_android_jni_headers/ResourceManager_jni.h"
29 #include "ui/android/window_android.h"
30 #include "ui/gfx/android/java_bitmap.h"
31 #include "ui/gfx/geometry/rect.h"
32
33 using base::android::JavaArrayOfIntArrayToIntVector;
34 using base::android::JavaParamRef;
35 using base::android::JavaRef;
36
37 namespace {
38
CreateMemoryDump(const std::string & name,size_t memory_usage,base::trace_event::ProcessMemoryDump * pmd)39 base::trace_event::MemoryAllocatorDump* CreateMemoryDump(
40 const std::string& name,
41 size_t memory_usage,
42 base::trace_event::ProcessMemoryDump* pmd) {
43 base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(name);
44 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
45 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
46 memory_usage);
47
48 static const char* system_allocator_name =
49 base::trace_event::MemoryDumpManager::GetInstance()
50 ->system_allocator_pool_name();
51 if (system_allocator_name)
52 pmd->AddSuballocation(dump->guid(), system_allocator_name);
53 return dump;
54 }
55
56 } // namespace
57
58 namespace ui {
59
60 // static
FromJavaObject(const JavaRef<jobject> & jobj)61 ResourceManagerImpl* ResourceManagerImpl::FromJavaObject(
62 const JavaRef<jobject>& jobj) {
63 return reinterpret_cast<ResourceManagerImpl*>(
64 Java_ResourceManager_getNativePtr(base::android::AttachCurrentThread(),
65 jobj));
66 }
67
ResourceManagerImpl(gfx::NativeWindow native_window)68 ResourceManagerImpl::ResourceManagerImpl(gfx::NativeWindow native_window)
69 : ui_resource_manager_(nullptr) {
70 JNIEnv* env = base::android::AttachCurrentThread();
71 java_obj_.Reset(
72 env, Java_ResourceManager_create(env, native_window->GetJavaObject(),
73 reinterpret_cast<intptr_t>(this))
74 .obj());
75 DCHECK(!java_obj_.is_null());
76 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
77 this, "android::ResourceManagerImpl",
78 base::ThreadTaskRunnerHandle::Get());
79 }
80
~ResourceManagerImpl()81 ResourceManagerImpl::~ResourceManagerImpl() {
82 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
83 this);
84 Java_ResourceManager_destroy(base::android::AttachCurrentThread(), java_obj_);
85 }
86
Init(cc::UIResourceManager * ui_resource_manager)87 void ResourceManagerImpl::Init(cc::UIResourceManager* ui_resource_manager) {
88 DCHECK(!ui_resource_manager_);
89 DCHECK(ui_resource_manager);
90 ui_resource_manager_ = ui_resource_manager;
91 }
92
93 base::android::ScopedJavaLocalRef<jobject>
GetJavaObject()94 ResourceManagerImpl::GetJavaObject() {
95 return base::android::ScopedJavaLocalRef<jobject>(java_obj_);
96 }
97
GetResource(AndroidResourceType res_type,int res_id)98 Resource* ResourceManagerImpl::GetResource(AndroidResourceType res_type,
99 int res_id) {
100 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
101 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
102
103 std::unordered_map<int, std::unique_ptr<Resource>>::iterator item =
104 resources_[res_type].find(res_id);
105
106 if (item == resources_[res_type].end() ||
107 res_type == ANDROID_RESOURCE_TYPE_DYNAMIC ||
108 res_type == ANDROID_RESOURCE_TYPE_DYNAMIC_BITMAP) {
109 RequestResourceFromJava(res_type, res_id);
110
111 // Check if the resource has been added (some dynamic may not have been).
112 item = resources_[res_type].find(res_id);
113 if (item == resources_[res_type].end())
114 return nullptr;
115 }
116
117 return item->second.get();
118 }
119
RemoveUnusedTints(const std::unordered_set<int> & used_tints)120 void ResourceManagerImpl::RemoveUnusedTints(
121 const std::unordered_set<int>& used_tints) {
122 // Iterate over the currently cached tints and remove ones that were not
123 // used as defined in |used_tints|.
124 for (auto it = tinted_resources_.cbegin(); it != tinted_resources_.cend();) {
125 if (used_tints.find(it->first) == used_tints.end()) {
126 it = tinted_resources_.erase(it);
127 } else {
128 ++it;
129 }
130 }
131 }
132
GetStaticResourceWithTint(int res_id,SkColor tint_color)133 Resource* ResourceManagerImpl::GetStaticResourceWithTint(int res_id,
134 SkColor tint_color) {
135 if (tinted_resources_.find(tint_color) == tinted_resources_.end()) {
136 tinted_resources_[tint_color] = std::make_unique<ResourceMap>();
137 }
138 ResourceMap* resource_map = tinted_resources_[tint_color].get();
139
140 // If the resource is already cached, use it.
141 std::unordered_map<int, std::unique_ptr<Resource>>::iterator item =
142 resource_map->find(res_id);
143 if (item != resource_map->end())
144 return item->second.get();
145
146 Resource* base_image = GetResource(ANDROID_RESOURCE_TYPE_STATIC, res_id);
147 DCHECK(base_image);
148
149 std::unique_ptr<Resource> tinted_resource = base_image->CreateForCopy();
150
151 TRACE_EVENT0("browser", "ResourceManagerImpl::GetStaticResourceWithTint");
152 SkBitmap tinted_bitmap;
153 tinted_bitmap.allocPixels(SkImageInfo::MakeN32Premul(
154 base_image->size().width(), base_image->size().height()));
155
156 SkCanvas canvas(tinted_bitmap);
157 canvas.clear(SK_ColorTRANSPARENT);
158
159 // Build a color filter to use on the base resource. This filter multiplies
160 // the RGB components by the components of the new color but retains the
161 // alpha of the original image.
162 float color_matrix[20] = {0, 0, 0, 0, SkColorGetR(tint_color) * (1.0f / 255),
163 0, 0, 0, 0, SkColorGetG(tint_color) * (1.0f / 255),
164 0, 0, 0, 0, SkColorGetB(tint_color) * (1.0f / 255),
165 0, 0, 0, 1, 0};
166 SkPaint color_filter;
167 color_filter.setColorFilter(SkColorFilters::Matrix(color_matrix));
168
169 // Draw the resource and make it immutable.
170 base_image->ui_resource()
171 ->GetBitmap(base_image->ui_resource()->id(), false)
172 .DrawToCanvas(&canvas, &color_filter);
173 tinted_bitmap.setImmutable();
174
175 // Create a UI resource from the new bitmap.
176 tinted_resource->SetUIResource(
177 cc::ScopedUIResource::Create(ui_resource_manager_,
178 cc::UIResourceBitmap(tinted_bitmap)),
179 base_image->size());
180
181 (*resource_map)[res_id].swap(tinted_resource);
182
183 return (*resource_map)[res_id].get();
184 }
185
ClearTintedResourceCache(JNIEnv * env,const JavaRef<jobject> & jobj)186 void ResourceManagerImpl::ClearTintedResourceCache(JNIEnv* env,
187 const JavaRef<jobject>& jobj) {
188 tinted_resources_.clear();
189 }
190
PreloadResource(AndroidResourceType res_type,int res_id)191 void ResourceManagerImpl::PreloadResource(AndroidResourceType res_type,
192 int res_id) {
193 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
194 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
195
196 // Don't send out a query if the resource is already loaded.
197 if (resources_[res_type].find(res_id) != resources_[res_type].end())
198 return;
199
200 PreloadResourceFromJava(res_type, res_id);
201 }
202
OnResourceReady(JNIEnv * env,const JavaRef<jobject> & jobj,jint res_type,jint res_id,const JavaRef<jobject> & bitmap,jint width,jint height,jlong native_resource)203 void ResourceManagerImpl::OnResourceReady(JNIEnv* env,
204 const JavaRef<jobject>& jobj,
205 jint res_type,
206 jint res_id,
207 const JavaRef<jobject>& bitmap,
208 jint width,
209 jint height,
210 jlong native_resource) {
211 DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
212 DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
213 TRACE_EVENT2("ui", "ResourceManagerImpl::OnResourceReady",
214 "resource_type", res_type,
215 "resource_id", res_id);
216
217 resources_[res_type][res_id] =
218 base::WrapUnique(reinterpret_cast<Resource*>(native_resource));
219 Resource* resource = resources_[res_type][res_id].get();
220
221 gfx::JavaBitmap jbitmap(bitmap);
222 SkBitmap skbitmap = gfx::CreateSkBitmapFromJavaBitmap(jbitmap);
223 skbitmap.setImmutable();
224 resource->SetUIResource(
225 cc::ScopedUIResource::Create(ui_resource_manager_,
226 cc::UIResourceBitmap(skbitmap)),
227 gfx::Size(width, height));
228 }
229
RemoveResource(JNIEnv * env,const base::android::JavaRef<jobject> & jobj,jint res_type,jint res_id)230 void ResourceManagerImpl::RemoveResource(
231 JNIEnv* env,
232 const base::android::JavaRef<jobject>& jobj,
233 jint res_type,
234 jint res_id) {
235 resources_[res_type].erase(res_id);
236 }
237
OnMemoryDump(const base::trace_event::MemoryDumpArgs & args,base::trace_event::ProcessMemoryDump * pmd)238 bool ResourceManagerImpl::OnMemoryDump(
239 const base::trace_event::MemoryDumpArgs& args,
240 base::trace_event::ProcessMemoryDump* pmd) {
241 std::string prefix = base::StringPrintf("ui/resource_manager_0x%" PRIXPTR,
242 reinterpret_cast<uintptr_t>(this));
243 for (uint32_t type = static_cast<uint32_t>(ANDROID_RESOURCE_TYPE_FIRST);
244 type <= static_cast<uint32_t>(ANDROID_RESOURCE_TYPE_LAST); ++type) {
245 size_t usage = base::trace_event::EstimateMemoryUsage(resources_[type]);
246 auto* dump = CreateMemoryDump(
247 prefix + base::StringPrintf("/default_resource/0x%u",
248 static_cast<uint32_t>(type)),
249 usage, pmd);
250 dump->AddScalar("resource_count", "objects", resources_[type].size());
251 }
252
253 size_t tinted_resource_usage =
254 base::trace_event::EstimateMemoryUsage(tinted_resources_);
255 CreateMemoryDump(prefix + "/tinted_resource", tinted_resource_usage, pmd);
256 return true;
257 }
258
PreloadResourceFromJava(AndroidResourceType res_type,int res_id)259 void ResourceManagerImpl::PreloadResourceFromJava(AndroidResourceType res_type,
260 int res_id) {
261 TRACE_EVENT2("ui", "ResourceManagerImpl::PreloadResourceFromJava",
262 "resource_type", res_type,
263 "resource_id", res_id);
264 Java_ResourceManager_preloadResource(base::android::AttachCurrentThread(),
265 java_obj_, res_type, res_id);
266 }
267
RequestResourceFromJava(AndroidResourceType res_type,int res_id)268 void ResourceManagerImpl::RequestResourceFromJava(AndroidResourceType res_type,
269 int res_id) {
270 TRACE_EVENT2("ui", "ResourceManagerImpl::RequestResourceFromJava",
271 "resource_type", res_type,
272 "resource_id", res_id);
273 Java_ResourceManager_resourceRequested(base::android::AttachCurrentThread(),
274 java_obj_, res_type, res_id);
275 }
276
277 } // namespace ui
278