OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/base/resource/resource_bundle.h" | |
6 | |
7 #include <limits> | |
8 #include <vector> | |
9 | |
10 #include "base/big_endian.h" | |
11 #include "base/command_line.h" | |
12 #include "base/files/file.h" | |
13 #include "base/files/file_util.h" | |
14 #include "base/logging.h" | |
15 #include "base/memory/ref_counted_memory.h" | |
16 #include "base/metrics/histogram.h" | |
17 #include "base/path_service.h" | |
18 #include "base/stl_util.h" | |
19 #include "base/strings/string_piece.h" | |
20 #include "base/strings/utf_string_conversions.h" | |
21 #include "base/synchronization/lock.h" | |
22 #include "build/build_config.h" | |
23 #include "skia/ext/image_operations.h" | |
24 #include "third_party/skia/include/core/SkBitmap.h" | |
25 #include "ui/base/l10n/l10n_util.h" | |
26 #include "ui/base/layout.h" | |
27 #include "ui/base/resource/data_pack.h" | |
28 #include "ui/base/ui_base_paths.h" | |
29 #include "ui/base/ui_base_switches.h" | |
30 #include "ui/gfx/codec/jpeg_codec.h" | |
31 #include "ui/gfx/codec/png_codec.h" | |
32 #include "ui/gfx/image/image_skia.h" | |
33 #include "ui/gfx/image/image_skia_source.h" | |
34 #include "ui/gfx/safe_integer_conversions.h" | |
35 #include "ui/gfx/screen.h" | |
36 #include "ui/gfx/size_conversions.h" | |
37 #include "ui/strings/grit/app_locale_settings.h" | |
38 | |
39 #if defined(OS_ANDROID) | |
40 #include "ui/base/resource/resource_bundle_android.h" | |
41 #endif | |
42 | |
43 #if defined(OS_CHROMEOS) | |
44 #include "ui/base/l10n/l10n_util.h" | |
45 #include "ui/gfx/platform_font_pango.h" | |
46 #endif | |
47 | |
48 #if defined(OS_WIN) | |
49 #include "ui/base/win/dpi_setup.h" | |
50 #include "ui/gfx/win/dpi.h" | |
51 #endif | |
52 | |
53 #if defined(OS_MACOSX) && !defined(OS_IOS) | |
54 #include "base/mac/mac_util.h" | |
55 #endif | |
56 | |
57 namespace ui { | |
58 | |
59 namespace { | |
60 | |
61 // Font sizes relative to base font. | |
62 const int kSmallFontSizeDelta = -1; | |
63 const int kMediumFontSizeDelta = 3; | |
64 const int kLargeFontSizeDelta = 8; | |
65 | |
66 // PNG-related constants. | |
67 const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; | |
68 const size_t kPngChunkMetadataSize = 12; // length, type, crc32 | |
69 const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' }; | |
70 const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' }; | |
71 | |
72 #if !defined(OS_MACOSX) | |
73 const char kPakFileSuffix[] = ".pak"; | |
74 #endif | |
75 | |
76 ResourceBundle* g_shared_instance_ = NULL; | |
77 | |
78 void InitDefaultFontList() { | |
79 #if defined(OS_CHROMEOS) && defined(USE_PANGO) | |
80 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
81 std::string font_family = base::UTF16ToUTF8( | |
82 rb.GetLocalizedString(IDS_UI_FONT_FAMILY_CROS)); | |
83 gfx::FontList::SetDefaultFontDescription(font_family); | |
84 | |
85 // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to | |
86 // the font list is done. We will no longer need SetDefaultFontDescription() | |
87 // after every client gets started using a FontList instead of a Font. | |
88 gfx::PlatformFontPango::SetDefaultFontDescription(font_family); | |
89 #else | |
90 // Use a single default font as the default font list. | |
91 gfx::FontList::SetDefaultFontDescription(std::string()); | |
92 #endif | |
93 } | |
94 | |
95 #if defined(OS_ANDROID) | |
96 // Returns the scale factor closest to |scale| from the full list of factors. | |
97 // Note that it does NOT rely on the list of supported scale factors. | |
98 // Finding the closest match is inefficient and shouldn't be done frequently. | |
99 ScaleFactor FindClosestScaleFactorUnsafe(float scale) { | |
100 float smallest_diff = std::numeric_limits<float>::max(); | |
101 ScaleFactor closest_match = SCALE_FACTOR_100P; | |
102 for (int i = SCALE_FACTOR_100P; i < NUM_SCALE_FACTORS; ++i) { | |
103 const ScaleFactor scale_factor = static_cast<ScaleFactor>(i); | |
104 float diff = std::abs(GetScaleForScaleFactor(scale_factor) - scale); | |
105 if (diff < smallest_diff) { | |
106 closest_match = scale_factor; | |
107 smallest_diff = diff; | |
108 } | |
109 } | |
110 return closest_match; | |
111 } | |
112 #endif // OS_ANDROID | |
113 | |
114 } // namespace | |
115 | |
116 // An ImageSkiaSource that loads bitmaps for the requested scale factor from | |
117 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the | |
118 // requested scale factor does not exist, it will return the 1x bitmap scaled | |
119 // by the scale factor. This may lead to broken UI if the correct size of the | |
120 // scaled image is not exactly |scale_factor| * the size of the 1x resource. | |
121 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images | |
122 // are higlighted by blending them with red. | |
123 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { | |
124 public: | |
125 ResourceBundleImageSource(ResourceBundle* rb, int resource_id) | |
126 : rb_(rb), resource_id_(resource_id) {} | |
127 ~ResourceBundleImageSource() override {} | |
128 | |
129 // gfx::ImageSkiaSource overrides: | |
130 gfx::ImageSkiaRep GetImageForScale(float scale) override { | |
131 SkBitmap image; | |
132 bool fell_back_to_1x = false; | |
133 ScaleFactor scale_factor = GetSupportedScaleFactor(scale); | |
134 bool found = rb_->LoadBitmap(resource_id_, &scale_factor, | |
135 &image, &fell_back_to_1x); | |
136 if (!found) | |
137 return gfx::ImageSkiaRep(); | |
138 | |
139 // If the resource is in the package with SCALE_FACTOR_NONE, it | |
140 // can be used in any scale factor. The image is maked as "unscaled" | |
141 // so that the ImageSkia do not automatically scale. | |
142 if (scale_factor == ui::SCALE_FACTOR_NONE) | |
143 return gfx::ImageSkiaRep(image, 0.0f); | |
144 | |
145 if (fell_back_to_1x) { | |
146 // GRIT fell back to the 100% image, so rescale it to the correct size. | |
147 image = skia::ImageOperations::Resize( | |
148 image, | |
149 skia::ImageOperations::RESIZE_LANCZOS3, | |
150 gfx::ToCeiledInt(image.width() * scale), | |
151 gfx::ToCeiledInt(image.height() * scale)); | |
152 } else { | |
153 scale = GetScaleForScaleFactor(scale_factor); | |
154 } | |
155 return gfx::ImageSkiaRep(image, scale); | |
156 } | |
157 | |
158 private: | |
159 ResourceBundle* rb_; | |
160 const int resource_id_; | |
161 | |
162 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); | |
163 }; | |
164 | |
165 // static | |
166 std::string ResourceBundle::InitSharedInstanceWithLocale( | |
167 const std::string& pref_locale, | |
168 Delegate* delegate, | |
169 LoadResources load_resources) { | |
170 InitSharedInstance(delegate); | |
171 if (load_resources == LOAD_COMMON_RESOURCES) | |
172 g_shared_instance_->LoadCommonResources(); | |
173 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale); | |
174 InitDefaultFontList(); | |
175 return result; | |
176 } | |
177 | |
178 // static | |
179 void ResourceBundle::InitSharedInstanceWithPakFileRegion( | |
180 base::File pak_file, | |
181 const base::MemoryMappedFile::Region& region) { | |
182 InitSharedInstance(NULL); | |
183 scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P)); | |
184 if (!data_pack->LoadFromFileRegion(pak_file.Pass(), region)) { | |
185 NOTREACHED() << "failed to load pak file"; | |
186 return; | |
187 } | |
188 g_shared_instance_->locale_resources_data_.reset(data_pack.release()); | |
189 InitDefaultFontList(); | |
190 } | |
191 | |
192 // static | |
193 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) { | |
194 InitSharedInstance(NULL); | |
195 g_shared_instance_->LoadTestResources(path, path); | |
196 | |
197 InitDefaultFontList(); | |
198 } | |
199 | |
200 // static | |
201 void ResourceBundle::CleanupSharedInstance() { | |
202 if (g_shared_instance_) { | |
203 delete g_shared_instance_; | |
204 g_shared_instance_ = NULL; | |
205 } | |
206 } | |
207 | |
208 // static | |
209 bool ResourceBundle::HasSharedInstance() { | |
210 return g_shared_instance_ != NULL; | |
211 } | |
212 | |
213 // static | |
214 ResourceBundle& ResourceBundle::GetSharedInstance() { | |
215 // Must call InitSharedInstance before this function. | |
216 CHECK(g_shared_instance_ != NULL); | |
217 return *g_shared_instance_; | |
218 } | |
219 | |
220 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { | |
221 bool locale_file_path_exists = !GetLocaleFilePath(locale, true).empty(); | |
222 #if defined(OS_ANDROID) | |
223 // TODO(mkosiba,primiano): Chrome should mmap the .pak files too, in which | |
224 // case we'd not need to check if locale_file_path_exists here. | |
225 // http://crbug.com/394502. | |
226 return locale_file_path_exists || | |
227 AssetContainedInApk(locale + kPakFileSuffix); | |
228 #else | |
229 return locale_file_path_exists; | |
230 #endif | |
231 } | |
232 | |
233 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path, | |
234 ScaleFactor scale_factor) { | |
235 AddDataPackFromPathInternal(path, scale_factor, false); | |
236 } | |
237 | |
238 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path, | |
239 ScaleFactor scale_factor) { | |
240 AddDataPackFromPathInternal(path, scale_factor, true); | |
241 } | |
242 | |
243 void ResourceBundle::AddDataPackFromFile(base::File file, | |
244 ScaleFactor scale_factor) { | |
245 AddDataPackFromFileRegion( | |
246 file.Pass(), base::MemoryMappedFile::Region::kWholeFile, scale_factor); | |
247 } | |
248 | |
249 void ResourceBundle::AddDataPackFromFileRegion( | |
250 base::File file, | |
251 const base::MemoryMappedFile::Region& region, | |
252 ScaleFactor scale_factor) { | |
253 scoped_ptr<DataPack> data_pack( | |
254 new DataPack(scale_factor)); | |
255 if (data_pack->LoadFromFileRegion(file.Pass(), region)) { | |
256 AddDataPack(data_pack.release()); | |
257 } else { | |
258 LOG(ERROR) << "Failed to load data pack from file." | |
259 << "\nSome features may not be available."; | |
260 } | |
261 } | |
262 | |
263 #if !defined(OS_MACOSX) | |
264 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, | |
265 bool test_file_exists) { | |
266 if (app_locale.empty()) | |
267 return base::FilePath(); | |
268 | |
269 base::FilePath locale_file_path; | |
270 | |
271 PathService::Get(ui::DIR_LOCALES, &locale_file_path); | |
272 | |
273 if (!locale_file_path.empty()) { | |
274 locale_file_path = | |
275 locale_file_path.AppendASCII(app_locale + kPakFileSuffix); | |
276 } | |
277 | |
278 if (delegate_) { | |
279 locale_file_path = | |
280 delegate_->GetPathForLocalePack(locale_file_path, app_locale); | |
281 } | |
282 | |
283 // Don't try to load empty values or values that are not absolute paths. | |
284 if (locale_file_path.empty() || !locale_file_path.IsAbsolute()) | |
285 return base::FilePath(); | |
286 | |
287 if (test_file_exists && !base::PathExists(locale_file_path)) | |
288 return base::FilePath(); | |
289 | |
290 return locale_file_path; | |
291 } | |
292 #endif | |
293 | |
294 std::string ResourceBundle::LoadLocaleResources( | |
295 const std::string& pref_locale) { | |
296 DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded"; | |
297 std::string app_locale = l10n_util::GetApplicationLocale(pref_locale); | |
298 base::FilePath locale_file_path = GetOverriddenPakPath(); | |
299 if (locale_file_path.empty()) | |
300 locale_file_path = GetLocaleFilePath(app_locale, true); | |
301 | |
302 if (locale_file_path.empty()) { | |
303 // It's possible that there is no locale.pak. | |
304 LOG(WARNING) << "locale_file_path.empty()"; | |
305 return std::string(); | |
306 } | |
307 | |
308 scoped_ptr<DataPack> data_pack( | |
309 new DataPack(SCALE_FACTOR_100P)); | |
310 if (!data_pack->LoadFromPath(locale_file_path)) { | |
311 UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError", | |
312 logging::GetLastSystemErrorCode(), 16000); | |
313 LOG(ERROR) << "failed to load locale.pak"; | |
314 NOTREACHED(); | |
315 return std::string(); | |
316 } | |
317 | |
318 locale_resources_data_.reset(data_pack.release()); | |
319 return app_locale; | |
320 } | |
321 | |
322 void ResourceBundle::LoadTestResources(const base::FilePath& path, | |
323 const base::FilePath& locale_path) { | |
324 DCHECK(!ui::GetSupportedScaleFactors().empty()); | |
325 const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]); | |
326 // Use the given resource pak for both common and localized resources. | |
327 scoped_ptr<DataPack> data_pack(new DataPack(scale_factor)); | |
328 if (!path.empty() && data_pack->LoadFromPath(path)) | |
329 AddDataPack(data_pack.release()); | |
330 | |
331 data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE)); | |
332 if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) { | |
333 locale_resources_data_.reset(data_pack.release()); | |
334 } else { | |
335 locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE)); | |
336 } | |
337 } | |
338 | |
339 void ResourceBundle::UnloadLocaleResources() { | |
340 locale_resources_data_.reset(); | |
341 } | |
342 | |
343 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) { | |
344 overridden_pak_path_ = pak_path; | |
345 } | |
346 | |
347 void ResourceBundle::OverrideLocaleStringResource( | |
348 int message_id, | |
349 const base::string16& string) { | |
350 overridden_locale_strings_[message_id] = string; | |
351 } | |
352 | |
353 const base::FilePath& ResourceBundle::GetOverriddenPakPath() { | |
354 return overridden_pak_path_; | |
355 } | |
356 | |
357 std::string ResourceBundle::ReloadLocaleResources( | |
358 const std::string& pref_locale) { | |
359 base::AutoLock lock_scope(*locale_resources_data_lock_); | |
360 | |
361 // Remove all overriden strings, as they will not be valid for the new locale. | |
362 overridden_locale_strings_.clear(); | |
363 | |
364 UnloadLocaleResources(); | |
365 return LoadLocaleResources(pref_locale); | |
366 } | |
367 | |
368 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) { | |
369 const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia(); | |
370 return const_cast<gfx::ImageSkia*>(image); | |
371 } | |
372 | |
373 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { | |
374 // Check to see if the image is already in the cache. | |
375 { | |
376 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
377 if (images_.count(resource_id)) | |
378 return images_[resource_id]; | |
379 } | |
380 | |
381 gfx::Image image; | |
382 if (delegate_) | |
383 image = delegate_->GetImageNamed(resource_id); | |
384 | |
385 if (image.IsEmpty()) { | |
386 DCHECK(!data_packs_.empty()) << | |
387 "Missing call to SetResourcesDataDLL?"; | |
388 | |
389 #if defined(OS_CHROMEOS) || defined(OS_WIN) | |
390 ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor(); | |
391 #else | |
392 ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; | |
393 #endif | |
394 | |
395 // TODO(oshima): Consider reading the image size from png IHDR chunk and | |
396 // skip decoding here and remove #ifdef below. | |
397 // ResourceBundle::GetSharedInstance() is destroyed after the | |
398 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be | |
399 // destroyed before the resource bundle is destroyed. | |
400 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id), | |
401 GetScaleForScaleFactor(scale_factor_to_load)); | |
402 if (image_skia.isNull()) { | |
403 LOG(WARNING) << "Unable to load image with id " << resource_id; | |
404 NOTREACHED(); // Want to assert in debug mode. | |
405 // The load failed to retrieve the image; show a debugging red square. | |
406 return GetEmptyImage(); | |
407 } | |
408 image_skia.SetReadOnly(); | |
409 image = gfx::Image(image_skia); | |
410 } | |
411 | |
412 // The load was successful, so cache the image. | |
413 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
414 | |
415 // Another thread raced the load and has already cached the image. | |
416 if (images_.count(resource_id)) | |
417 return images_[resource_id]; | |
418 | |
419 images_[resource_id] = image; | |
420 return images_[resource_id]; | |
421 } | |
422 | |
423 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { | |
424 return GetNativeImageNamed(resource_id, RTL_DISABLED); | |
425 } | |
426 | |
427 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes( | |
428 int resource_id) const { | |
429 return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE); | |
430 } | |
431 | |
432 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale( | |
433 int resource_id, | |
434 ScaleFactor scale_factor) const { | |
435 base::RefCountedStaticMemory* bytes = NULL; | |
436 if (delegate_) | |
437 bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor); | |
438 | |
439 if (!bytes) { | |
440 base::StringPiece data = | |
441 GetRawDataResourceForScale(resource_id, scale_factor); | |
442 if (!data.empty()) { | |
443 bytes = new base::RefCountedStaticMemory(data.data(), data.length()); | |
444 } | |
445 } | |
446 | |
447 return bytes; | |
448 } | |
449 | |
450 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const { | |
451 return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE); | |
452 } | |
453 | |
454 base::StringPiece ResourceBundle::GetRawDataResourceForScale( | |
455 int resource_id, | |
456 ScaleFactor scale_factor) const { | |
457 base::StringPiece data; | |
458 if (delegate_ && | |
459 delegate_->GetRawDataResource(resource_id, scale_factor, &data)) | |
460 return data; | |
461 | |
462 if (scale_factor != ui::SCALE_FACTOR_100P) { | |
463 for (size_t i = 0; i < data_packs_.size(); i++) { | |
464 if (data_packs_[i]->GetScaleFactor() == scale_factor && | |
465 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id), | |
466 &data)) | |
467 return data; | |
468 } | |
469 } | |
470 for (size_t i = 0; i < data_packs_.size(); i++) { | |
471 if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P || | |
472 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P || | |
473 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) && | |
474 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id), | |
475 &data)) | |
476 return data; | |
477 } | |
478 | |
479 return base::StringPiece(); | |
480 } | |
481 | |
482 base::string16 ResourceBundle::GetLocalizedString(int message_id) { | |
483 base::string16 string; | |
484 if (delegate_ && delegate_->GetLocalizedString(message_id, &string)) | |
485 return string; | |
486 | |
487 // Ensure that ReloadLocaleResources() doesn't drop the resources while | |
488 // we're using them. | |
489 base::AutoLock lock_scope(*locale_resources_data_lock_); | |
490 | |
491 IdToStringMap::const_iterator it = | |
492 overridden_locale_strings_.find(message_id); | |
493 if (it != overridden_locale_strings_.end()) | |
494 return it->second; | |
495 | |
496 // If for some reason we were unable to load the resources , return an empty | |
497 // string (better than crashing). | |
498 if (!locale_resources_data_.get()) { | |
499 LOG(WARNING) << "locale resources are not loaded"; | |
500 return base::string16(); | |
501 } | |
502 | |
503 base::StringPiece data; | |
504 if (!locale_resources_data_->GetStringPiece(static_cast<uint16>(message_id), | |
505 &data)) { | |
506 // Fall back on the main data pack (shouldn't be any strings here except in | |
507 // unittests). | |
508 data = GetRawDataResource(message_id); | |
509 if (data.empty()) { | |
510 NOTREACHED() << "unable to find resource: " << message_id; | |
511 return base::string16(); | |
512 } | |
513 } | |
514 | |
515 // Strings should not be loaded from a data pack that contains binary data. | |
516 ResourceHandle::TextEncodingType encoding = | |
517 locale_resources_data_->GetTextEncodingType(); | |
518 DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) | |
519 << "requested localized string from binary pack file"; | |
520 | |
521 // Data pack encodes strings as either UTF8 or UTF16. | |
522 base::string16 msg; | |
523 if (encoding == ResourceHandle::UTF16) { | |
524 msg = base::string16(reinterpret_cast<const base::char16*>(data.data()), | |
525 data.length() / 2); | |
526 } else if (encoding == ResourceHandle::UTF8) { | |
527 msg = base::UTF8ToUTF16(data); | |
528 } | |
529 return msg; | |
530 } | |
531 | |
532 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) { | |
533 { | |
534 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
535 LoadFontsIfNecessary(); | |
536 } | |
537 switch (style) { | |
538 case BoldFont: | |
539 return *bold_font_list_; | |
540 case SmallFont: | |
541 return *small_font_list_; | |
542 case MediumFont: | |
543 return *medium_font_list_; | |
544 case SmallBoldFont: | |
545 return *small_bold_font_list_; | |
546 case MediumBoldFont: | |
547 return *medium_bold_font_list_; | |
548 case LargeFont: | |
549 return *large_font_list_; | |
550 case LargeBoldFont: | |
551 return *large_bold_font_list_; | |
552 default: | |
553 return *base_font_list_; | |
554 } | |
555 } | |
556 | |
557 const gfx::Font& ResourceBundle::GetFont(FontStyle style) { | |
558 return GetFontList(style).GetPrimaryFont(); | |
559 } | |
560 | |
561 void ResourceBundle::ReloadFonts() { | |
562 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
563 base_font_list_.reset(); | |
564 LoadFontsIfNecessary(); | |
565 } | |
566 | |
567 ScaleFactor ResourceBundle::GetMaxScaleFactor() const { | |
568 #if defined(OS_CHROMEOS) || defined(OS_WIN) | |
569 return max_scale_factor_; | |
570 #else | |
571 return GetSupportedScaleFactors().back(); | |
572 #endif | |
573 } | |
574 | |
575 bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) { | |
576 const std::vector<ScaleFactor>& supported_scale_factors = | |
577 ui::GetSupportedScaleFactors(); | |
578 return std::find(supported_scale_factors.begin(), | |
579 supported_scale_factors.end(), | |
580 scale_factor) != supported_scale_factors.end(); | |
581 } | |
582 | |
583 ResourceBundle::ResourceBundle(Delegate* delegate) | |
584 : delegate_(delegate), | |
585 images_and_fonts_lock_(new base::Lock), | |
586 locale_resources_data_lock_(new base::Lock), | |
587 max_scale_factor_(SCALE_FACTOR_100P) { | |
588 } | |
589 | |
590 ResourceBundle::~ResourceBundle() { | |
591 FreeImages(); | |
592 UnloadLocaleResources(); | |
593 } | |
594 | |
595 // static | |
596 void ResourceBundle::InitSharedInstance(Delegate* delegate) { | |
597 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; | |
598 g_shared_instance_ = new ResourceBundle(delegate); | |
599 static std::vector<ScaleFactor> supported_scale_factors; | |
600 #if !defined(OS_IOS) && !defined(OS_WIN) | |
601 // On platforms other than iOS, 100P is always a supported scale factor. | |
602 // For Windows we have a separate case in this function. | |
603 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
604 #endif | |
605 #if defined(OS_ANDROID) | |
606 const gfx::Display display = | |
607 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); | |
608 const float display_density = display.device_scale_factor(); | |
609 const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density); | |
610 if (closest != SCALE_FACTOR_100P) | |
611 supported_scale_factors.push_back(closest); | |
612 #elif defined(OS_IOS) | |
613 gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); | |
614 if (display.device_scale_factor() > 2.0) { | |
615 DCHECK_EQ(3.0, display.device_scale_factor()); | |
616 supported_scale_factors.push_back(SCALE_FACTOR_300P); | |
617 } else if (display.device_scale_factor() > 1.0) { | |
618 DCHECK_EQ(2.0, display.device_scale_factor()); | |
619 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
620 } else { | |
621 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
622 } | |
623 #elif defined(OS_MACOSX) | |
624 if (base::mac::IsOSLionOrLater()) | |
625 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
626 #elif defined(OS_CHROMEOS) | |
627 // TODO(oshima): Include 200P only if the device support 200P | |
628 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
629 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI) | |
630 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
631 #elif defined(OS_WIN) | |
632 bool default_to_100P = true; | |
633 if (gfx::IsHighDPIEnabled()) { | |
634 // On Windows if the dpi scale is greater than 1.25 on high dpi machines | |
635 // downscaling from 200 percent looks better than scaling up from 100 | |
636 // percent. | |
637 if (gfx::GetDPIScale() > 1.25) { | |
638 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
639 default_to_100P = false; | |
640 } | |
641 } | |
642 if (default_to_100P) | |
643 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
644 #endif | |
645 ui::SetSupportedScaleFactors(supported_scale_factors); | |
646 #if defined(OS_WIN) | |
647 // Must be called _after_ supported scale factors are set since it | |
648 // uses them. | |
649 // Don't initialize the device scale factor if it has already been | |
650 // initialized. | |
651 if (!gfx::win::IsDeviceScaleFactorSet()) | |
652 ui::win::InitDeviceScaleFactor(); | |
653 #endif | |
654 } | |
655 | |
656 void ResourceBundle::FreeImages() { | |
657 images_.clear(); | |
658 } | |
659 | |
660 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path, | |
661 ScaleFactor scale_factor, | |
662 bool optional) { | |
663 // Do not pass an empty |path| value to this method. If the absolute path is | |
664 // unknown pass just the pack file name. | |
665 DCHECK(!path.empty()); | |
666 | |
667 base::FilePath pack_path = path; | |
668 if (delegate_) | |
669 pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor); | |
670 | |
671 // Don't try to load empty values or values that are not absolute paths. | |
672 if (pack_path.empty() || !pack_path.IsAbsolute()) | |
673 return; | |
674 | |
675 scoped_ptr<DataPack> data_pack( | |
676 new DataPack(scale_factor)); | |
677 if (data_pack->LoadFromPath(pack_path)) { | |
678 AddDataPack(data_pack.release()); | |
679 } else if (!optional) { | |
680 LOG(ERROR) << "Failed to load " << pack_path.value() | |
681 << "\nSome features may not be available."; | |
682 } | |
683 } | |
684 | |
685 void ResourceBundle::AddDataPack(DataPack* data_pack) { | |
686 data_packs_.push_back(data_pack); | |
687 | |
688 if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) > | |
689 GetScaleForScaleFactor(max_scale_factor_)) | |
690 max_scale_factor_ = data_pack->GetScaleFactor(); | |
691 } | |
692 | |
693 void ResourceBundle::LoadFontsIfNecessary() { | |
694 images_and_fonts_lock_->AssertAcquired(); | |
695 if (!base_font_list_.get()) { | |
696 if (delegate_) { | |
697 base_font_list_ = GetFontListFromDelegate(BaseFont); | |
698 bold_font_list_ = GetFontListFromDelegate(BoldFont); | |
699 small_font_list_ = GetFontListFromDelegate(SmallFont); | |
700 small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont); | |
701 medium_font_list_ = GetFontListFromDelegate(MediumFont); | |
702 medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont); | |
703 large_font_list_ = GetFontListFromDelegate(LargeFont); | |
704 large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont); | |
705 } | |
706 | |
707 if (!base_font_list_.get()) | |
708 base_font_list_.reset(new gfx::FontList()); | |
709 | |
710 if (!bold_font_list_.get()) { | |
711 bold_font_list_.reset(new gfx::FontList()); | |
712 *bold_font_list_ = base_font_list_->DeriveWithStyle( | |
713 base_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
714 } | |
715 | |
716 if (!small_font_list_.get()) { | |
717 small_font_list_.reset(new gfx::FontList()); | |
718 *small_font_list_ = | |
719 base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta); | |
720 } | |
721 | |
722 if (!small_bold_font_list_.get()) { | |
723 small_bold_font_list_.reset(new gfx::FontList()); | |
724 *small_bold_font_list_ = small_font_list_->DeriveWithStyle( | |
725 small_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
726 } | |
727 | |
728 if (!medium_font_list_.get()) { | |
729 medium_font_list_.reset(new gfx::FontList()); | |
730 *medium_font_list_ = | |
731 base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta); | |
732 } | |
733 | |
734 if (!medium_bold_font_list_.get()) { | |
735 medium_bold_font_list_.reset(new gfx::FontList()); | |
736 *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle( | |
737 medium_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
738 } | |
739 | |
740 if (!large_font_list_.get()) { | |
741 large_font_list_.reset(new gfx::FontList()); | |
742 *large_font_list_ = | |
743 base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta); | |
744 } | |
745 | |
746 if (!large_bold_font_list_.get()) { | |
747 large_bold_font_list_.reset(new gfx::FontList()); | |
748 *large_bold_font_list_ = large_font_list_->DeriveWithStyle( | |
749 large_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
750 } | |
751 } | |
752 } | |
753 | |
754 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate( | |
755 FontStyle style) { | |
756 DCHECK(delegate_); | |
757 scoped_ptr<gfx::Font> font = delegate_->GetFont(style); | |
758 if (font.get()) | |
759 return scoped_ptr<gfx::FontList>(new gfx::FontList(*font)); | |
760 return scoped_ptr<gfx::FontList>(); | |
761 } | |
762 | |
763 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, | |
764 int resource_id, | |
765 SkBitmap* bitmap, | |
766 bool* fell_back_to_1x) const { | |
767 DCHECK(fell_back_to_1x); | |
768 scoped_refptr<base::RefCountedMemory> memory( | |
769 data_handle.GetStaticMemory(static_cast<uint16>(resource_id))); | |
770 if (!memory.get()) | |
771 return false; | |
772 | |
773 if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x)) | |
774 return true; | |
775 | |
776 #if !defined(OS_IOS) | |
777 // iOS does not compile or use the JPEG codec. On other platforms, | |
778 // 99% of our assets are PNGs, however fallback to JPEG. | |
779 scoped_ptr<SkBitmap> jpeg_bitmap( | |
780 gfx::JPEGCodec::Decode(memory->front(), memory->size())); | |
781 if (jpeg_bitmap.get()) { | |
782 bitmap->swap(*jpeg_bitmap.get()); | |
783 *fell_back_to_1x = false; | |
784 return true; | |
785 } | |
786 #endif | |
787 | |
788 NOTREACHED() << "Unable to decode theme image resource " << resource_id; | |
789 return false; | |
790 } | |
791 | |
792 bool ResourceBundle::LoadBitmap(int resource_id, | |
793 ScaleFactor* scale_factor, | |
794 SkBitmap* bitmap, | |
795 bool* fell_back_to_1x) const { | |
796 DCHECK(fell_back_to_1x); | |
797 for (size_t i = 0; i < data_packs_.size(); ++i) { | |
798 if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE && | |
799 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) { | |
800 DCHECK(!*fell_back_to_1x); | |
801 *scale_factor = ui::SCALE_FACTOR_NONE; | |
802 return true; | |
803 } | |
804 if (data_packs_[i]->GetScaleFactor() == *scale_factor && | |
805 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) { | |
806 return true; | |
807 } | |
808 } | |
809 return false; | |
810 } | |
811 | |
812 gfx::Image& ResourceBundle::GetEmptyImage() { | |
813 base::AutoLock lock(*images_and_fonts_lock_); | |
814 | |
815 if (empty_image_.IsEmpty()) { | |
816 // The placeholder bitmap is bright red so people notice the problem. | |
817 SkBitmap bitmap; | |
818 bitmap.allocN32Pixels(32, 32); | |
819 bitmap.eraseARGB(255, 255, 0, 0); | |
820 empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap); | |
821 } | |
822 return empty_image_; | |
823 } | |
824 | |
825 // static | |
826 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf, | |
827 size_t size) { | |
828 if (size < arraysize(kPngMagic) || | |
829 memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) { | |
830 // Data invalid or a JPEG. | |
831 return false; | |
832 } | |
833 size_t pos = arraysize(kPngMagic); | |
834 | |
835 // Scan for custom chunks until we find one, find the IDAT chunk, or run out | |
836 // of chunks. | |
837 for (;;) { | |
838 if (size - pos < kPngChunkMetadataSize) | |
839 break; | |
840 uint32 length = 0; | |
841 base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length); | |
842 if (size - pos - kPngChunkMetadataSize < length) | |
843 break; | |
844 if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType, | |
845 arraysize(kPngScaleChunkType)) == 0) { | |
846 return true; | |
847 } | |
848 if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType, | |
849 arraysize(kPngDataChunkType)) == 0) { | |
850 // Stop looking for custom chunks, any custom chunks should be before an | |
851 // IDAT chunk. | |
852 break; | |
853 } | |
854 pos += length + kPngChunkMetadataSize; | |
855 } | |
856 return false; | |
857 } | |
858 | |
859 // static | |
860 bool ResourceBundle::DecodePNG(const unsigned char* buf, | |
861 size_t size, | |
862 SkBitmap* bitmap, | |
863 bool* fell_back_to_1x) { | |
864 *fell_back_to_1x = PNGContainsFallbackMarker(buf, size); | |
865 return gfx::PNGCodec::Decode(buf, size, bitmap); | |
866 } | |
867 | |
868 } // namespace ui | |
OLD | NEW |