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.
5 #include "ui/base/resource/resource_bundle.h"
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/geometry/safe_integer_conversions.h"
33 #include "ui/gfx/geometry/size_conversions.h"
34 #include "ui/gfx/image/image_skia.h"
35 #include "ui/gfx/image/image_skia_source.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/strings/grit/app_locale_settings.h"
39 #if defined(OS_ANDROID)
40 #include "ui/base/resource/resource_bundle_android.h"
43 #if defined(OS_CHROMEOS)
44 #include "ui/gfx/platform_font_linux.h"
48 #include "ui/gfx/win/dpi.h"
51 #if defined(OS_MACOSX) && !defined(OS_IOS)
52 #include "base/mac/mac_util.h"
59 // Font sizes relative to base font.
60 const int kSmallFontSizeDelta
= -1;
61 const int kMediumFontSizeDelta
= 3;
62 const int kLargeFontSizeDelta
= 8;
64 // PNG-related constants.
65 const unsigned char kPngMagic
[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
66 const size_t kPngChunkMetadataSize
= 12; // length, type, crc32
67 const unsigned char kPngScaleChunkType
[4] = { 'c', 's', 'C', 'l' };
68 const unsigned char kPngDataChunkType
[4] = { 'I', 'D', 'A', 'T' };
70 #if !defined(OS_MACOSX)
71 const char kPakFileSuffix
[] = ".pak";
74 ResourceBundle
* g_shared_instance_
= NULL
;
76 #if defined(OS_ANDROID)
77 // Returns the scale factor closest to |scale| from the full list of factors.
78 // Note that it does NOT rely on the list of supported scale factors.
79 // Finding the closest match is inefficient and shouldn't be done frequently.
80 ScaleFactor
FindClosestScaleFactorUnsafe(float scale
) {
81 float smallest_diff
= std::numeric_limits
<float>::max();
82 ScaleFactor closest_match
= SCALE_FACTOR_100P
;
83 for (int i
= SCALE_FACTOR_100P
; i
< NUM_SCALE_FACTORS
; ++i
) {
84 const ScaleFactor scale_factor
= static_cast<ScaleFactor
>(i
);
85 float diff
= std::abs(GetScaleForScaleFactor(scale_factor
) - scale
);
86 if (diff
< smallest_diff
) {
87 closest_match
= scale_factor
;
97 // An ImageSkiaSource that loads bitmaps for the requested scale factor from
98 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the
99 // requested scale factor does not exist, it will return the 1x bitmap scaled
100 // by the scale factor. This may lead to broken UI if the correct size of the
101 // scaled image is not exactly |scale_factor| * the size of the 1x resource.
102 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images
103 // are higlighted by blending them with red.
104 class ResourceBundle::ResourceBundleImageSource
: public gfx::ImageSkiaSource
{
106 ResourceBundleImageSource(ResourceBundle
* rb
, int resource_id
)
107 : rb_(rb
), resource_id_(resource_id
) {}
108 ~ResourceBundleImageSource() override
{}
110 // gfx::ImageSkiaSource overrides:
111 gfx::ImageSkiaRep
GetImageForScale(float scale
) override
{
113 bool fell_back_to_1x
= false;
114 ScaleFactor scale_factor
= GetSupportedScaleFactor(scale
);
115 bool found
= rb_
->LoadBitmap(resource_id_
, &scale_factor
,
116 &image
, &fell_back_to_1x
);
118 return gfx::ImageSkiaRep();
120 // If the resource is in the package with SCALE_FACTOR_NONE, it
121 // can be used in any scale factor. The image is maked as "unscaled"
122 // so that the ImageSkia do not automatically scale.
123 if (scale_factor
== ui::SCALE_FACTOR_NONE
)
124 return gfx::ImageSkiaRep(image
, 0.0f
);
126 if (fell_back_to_1x
) {
127 // GRIT fell back to the 100% image, so rescale it to the correct size.
128 image
= skia::ImageOperations::Resize(
130 skia::ImageOperations::RESIZE_LANCZOS3
,
131 gfx::ToCeiledInt(image
.width() * scale
),
132 gfx::ToCeiledInt(image
.height() * scale
));
134 scale
= GetScaleForScaleFactor(scale_factor
);
136 return gfx::ImageSkiaRep(image
, scale
);
141 const int resource_id_
;
143 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource
);
147 std::string
ResourceBundle::InitSharedInstanceWithLocale(
148 const std::string
& pref_locale
,
150 LoadResources load_resources
) {
151 InitSharedInstance(delegate
);
152 if (load_resources
== LOAD_COMMON_RESOURCES
)
153 g_shared_instance_
->LoadCommonResources();
154 std::string result
= g_shared_instance_
->LoadLocaleResources(pref_locale
);
155 g_shared_instance_
->InitDefaultFontList();
160 void ResourceBundle::InitSharedInstanceWithPakFileRegion(
162 const base::MemoryMappedFile::Region
& region
) {
163 InitSharedInstance(NULL
);
164 scoped_ptr
<DataPack
> data_pack(new DataPack(SCALE_FACTOR_100P
));
165 if (!data_pack
->LoadFromFileRegion(pak_file
.Pass(), region
)) {
166 NOTREACHED() << "failed to load pak file";
169 g_shared_instance_
->locale_resources_data_
.reset(data_pack
.release());
170 g_shared_instance_
->InitDefaultFontList();
174 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath
& path
) {
175 InitSharedInstance(NULL
);
176 g_shared_instance_
->LoadTestResources(path
, path
);
178 g_shared_instance_
->InitDefaultFontList();
182 void ResourceBundle::CleanupSharedInstance() {
183 if (g_shared_instance_
) {
184 delete g_shared_instance_
;
185 g_shared_instance_
= NULL
;
190 bool ResourceBundle::HasSharedInstance() {
191 return g_shared_instance_
!= NULL
;
195 ResourceBundle
& ResourceBundle::GetSharedInstance() {
196 // Must call InitSharedInstance before this function.
197 CHECK(g_shared_instance_
!= NULL
);
198 return *g_shared_instance_
;
201 bool ResourceBundle::LocaleDataPakExists(const std::string
& locale
) {
202 bool locale_file_path_exists
= !GetLocaleFilePath(locale
, true).empty();
203 #if defined(OS_ANDROID)
204 // TODO(mkosiba,primiano): Chrome should mmap the .pak files too, in which
205 // case we'd not need to check if locale_file_path_exists here.
206 // http://crbug.com/394502.
207 return locale_file_path_exists
||
208 AssetContainedInApk(locale
+ kPakFileSuffix
);
210 return locale_file_path_exists
;
214 void ResourceBundle::AddDataPackFromPath(const base::FilePath
& path
,
215 ScaleFactor scale_factor
) {
216 AddDataPackFromPathInternal(path
, scale_factor
, false);
219 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath
& path
,
220 ScaleFactor scale_factor
) {
221 AddDataPackFromPathInternal(path
, scale_factor
, true);
224 void ResourceBundle::AddDataPackFromFile(base::File file
,
225 ScaleFactor scale_factor
) {
226 AddDataPackFromFileRegion(
227 file
.Pass(), base::MemoryMappedFile::Region::kWholeFile
, scale_factor
);
230 void ResourceBundle::AddDataPackFromFileRegion(
232 const base::MemoryMappedFile::Region
& region
,
233 ScaleFactor scale_factor
) {
234 scoped_ptr
<DataPack
> data_pack(
235 new DataPack(scale_factor
));
236 if (data_pack
->LoadFromFileRegion(file
.Pass(), region
)) {
237 AddDataPack(data_pack
.release());
239 LOG(ERROR
) << "Failed to load data pack from file."
240 << "\nSome features may not be available.";
244 #if !defined(OS_MACOSX)
245 base::FilePath
ResourceBundle::GetLocaleFilePath(const std::string
& app_locale
,
246 bool test_file_exists
) {
247 if (app_locale
.empty())
248 return base::FilePath();
250 base::FilePath locale_file_path
;
252 PathService::Get(ui::DIR_LOCALES
, &locale_file_path
);
254 if (!locale_file_path
.empty()) {
256 locale_file_path
.AppendASCII(app_locale
+ kPakFileSuffix
);
261 delegate_
->GetPathForLocalePack(locale_file_path
, app_locale
);
264 // Don't try to load empty values or values that are not absolute paths.
265 if (locale_file_path
.empty() || !locale_file_path
.IsAbsolute())
266 return base::FilePath();
268 if (test_file_exists
&& !base::PathExists(locale_file_path
))
269 return base::FilePath();
271 return locale_file_path
;
275 std::string
ResourceBundle::LoadLocaleResources(
276 const std::string
& pref_locale
) {
277 DCHECK(!locale_resources_data_
.get()) << "locale.pak already loaded";
278 std::string app_locale
= l10n_util::GetApplicationLocale(pref_locale
);
279 base::FilePath locale_file_path
= GetOverriddenPakPath();
280 if (locale_file_path
.empty())
281 locale_file_path
= GetLocaleFilePath(app_locale
, true);
283 if (locale_file_path
.empty()) {
284 // It's possible that there is no locale.pak.
285 LOG(WARNING
) << "locale_file_path.empty()";
286 return std::string();
289 scoped_ptr
<DataPack
> data_pack(
290 new DataPack(SCALE_FACTOR_100P
));
291 if (!data_pack
->LoadFromPath(locale_file_path
)) {
292 UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError",
293 logging::GetLastSystemErrorCode(), 16000);
294 LOG(ERROR
) << "failed to load locale.pak";
296 return std::string();
299 locale_resources_data_
.reset(data_pack
.release());
303 void ResourceBundle::LoadTestResources(const base::FilePath
& path
,
304 const base::FilePath
& locale_path
) {
305 DCHECK(!ui::GetSupportedScaleFactors().empty());
306 const ScaleFactor
scale_factor(ui::GetSupportedScaleFactors()[0]);
307 // Use the given resource pak for both common and localized resources.
308 scoped_ptr
<DataPack
> data_pack(new DataPack(scale_factor
));
309 if (!path
.empty() && data_pack
->LoadFromPath(path
))
310 AddDataPack(data_pack
.release());
312 data_pack
.reset(new DataPack(ui::SCALE_FACTOR_NONE
));
313 if (!locale_path
.empty() && data_pack
->LoadFromPath(locale_path
)) {
314 locale_resources_data_
.reset(data_pack
.release());
316 locale_resources_data_
.reset(new DataPack(ui::SCALE_FACTOR_NONE
));
320 void ResourceBundle::UnloadLocaleResources() {
321 locale_resources_data_
.reset();
324 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath
& pak_path
) {
325 overridden_pak_path_
= pak_path
;
328 void ResourceBundle::OverrideLocaleStringResource(
330 const base::string16
& string
) {
331 overridden_locale_strings_
[message_id
] = string
;
334 const base::FilePath
& ResourceBundle::GetOverriddenPakPath() {
335 return overridden_pak_path_
;
338 std::string
ResourceBundle::ReloadLocaleResources(
339 const std::string
& pref_locale
) {
340 base::AutoLock
lock_scope(*locale_resources_data_lock_
);
342 // Remove all overriden strings, as they will not be valid for the new locale.
343 overridden_locale_strings_
.clear();
345 UnloadLocaleResources();
346 return LoadLocaleResources(pref_locale
);
349 gfx::ImageSkia
* ResourceBundle::GetImageSkiaNamed(int resource_id
) {
350 const gfx::ImageSkia
* image
= GetImageNamed(resource_id
).ToImageSkia();
351 return const_cast<gfx::ImageSkia
*>(image
);
354 gfx::Image
& ResourceBundle::GetImageNamed(int resource_id
) {
355 // Check to see if the image is already in the cache.
357 base::AutoLock
lock_scope(*images_and_fonts_lock_
);
358 if (images_
.count(resource_id
))
359 return images_
[resource_id
];
364 image
= delegate_
->GetImageNamed(resource_id
);
366 if (image
.IsEmpty()) {
367 DCHECK(!data_packs_
.empty()) <<
368 "Missing call to SetResourcesDataDLL?";
370 #if defined(OS_CHROMEOS) || defined(OS_WIN)
371 ui::ScaleFactor scale_factor_to_load
= GetMaxScaleFactor();
373 ui::ScaleFactor scale_factor_to_load
= ui::SCALE_FACTOR_100P
;
376 // TODO(oshima): Consider reading the image size from png IHDR chunk and
377 // skip decoding here and remove #ifdef below.
378 // ResourceBundle::GetSharedInstance() is destroyed after the
379 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be
380 // destroyed before the resource bundle is destroyed.
381 gfx::ImageSkia
image_skia(new ResourceBundleImageSource(this, resource_id
),
382 GetScaleForScaleFactor(scale_factor_to_load
));
383 if (image_skia
.isNull()) {
384 LOG(WARNING
) << "Unable to load image with id " << resource_id
;
385 NOTREACHED(); // Want to assert in debug mode.
386 // The load failed to retrieve the image; show a debugging red square.
387 return GetEmptyImage();
389 image_skia
.SetReadOnly();
390 image
= gfx::Image(image_skia
);
393 // The load was successful, so cache the image.
394 base::AutoLock
lock_scope(*images_and_fonts_lock_
);
396 // Another thread raced the load and has already cached the image.
397 if (images_
.count(resource_id
))
398 return images_
[resource_id
];
400 images_
[resource_id
] = image
;
401 return images_
[resource_id
];
404 gfx::Image
& ResourceBundle::GetNativeImageNamed(int resource_id
) {
405 return GetNativeImageNamed(resource_id
, RTL_DISABLED
);
408 base::RefCountedStaticMemory
* ResourceBundle::LoadDataResourceBytes(
409 int resource_id
) const {
410 return LoadDataResourceBytesForScale(resource_id
, ui::SCALE_FACTOR_NONE
);
413 base::RefCountedStaticMemory
* ResourceBundle::LoadDataResourceBytesForScale(
415 ScaleFactor scale_factor
) const {
416 base::RefCountedStaticMemory
* bytes
= NULL
;
418 bytes
= delegate_
->LoadDataResourceBytes(resource_id
, scale_factor
);
421 base::StringPiece data
=
422 GetRawDataResourceForScale(resource_id
, scale_factor
);
424 bytes
= new base::RefCountedStaticMemory(data
.data(), data
.length());
431 base::StringPiece
ResourceBundle::GetRawDataResource(int resource_id
) const {
432 return GetRawDataResourceForScale(resource_id
, ui::SCALE_FACTOR_NONE
);
435 base::StringPiece
ResourceBundle::GetRawDataResourceForScale(
437 ScaleFactor scale_factor
) const {
438 base::StringPiece data
;
440 delegate_
->GetRawDataResource(resource_id
, scale_factor
, &data
))
443 if (scale_factor
!= ui::SCALE_FACTOR_100P
) {
444 for (size_t i
= 0; i
< data_packs_
.size(); i
++) {
445 if (data_packs_
[i
]->GetScaleFactor() == scale_factor
&&
446 data_packs_
[i
]->GetStringPiece(static_cast<uint16
>(resource_id
),
452 for (size_t i
= 0; i
< data_packs_
.size(); i
++) {
453 if ((data_packs_
[i
]->GetScaleFactor() == ui::SCALE_FACTOR_100P
||
454 data_packs_
[i
]->GetScaleFactor() == ui::SCALE_FACTOR_200P
||
455 data_packs_
[i
]->GetScaleFactor() == ui::SCALE_FACTOR_300P
||
456 data_packs_
[i
]->GetScaleFactor() == ui::SCALE_FACTOR_NONE
) &&
457 data_packs_
[i
]->GetStringPiece(static_cast<uint16
>(resource_id
),
462 return base::StringPiece();
465 base::string16
ResourceBundle::GetLocalizedString(int message_id
) {
466 base::string16 string
;
467 if (delegate_
&& delegate_
->GetLocalizedString(message_id
, &string
))
470 // Ensure that ReloadLocaleResources() doesn't drop the resources while
472 base::AutoLock
lock_scope(*locale_resources_data_lock_
);
474 IdToStringMap::const_iterator it
=
475 overridden_locale_strings_
.find(message_id
);
476 if (it
!= overridden_locale_strings_
.end())
479 // If for some reason we were unable to load the resources , return an empty
480 // string (better than crashing).
481 if (!locale_resources_data_
.get()) {
482 LOG(WARNING
) << "locale resources are not loaded";
483 return base::string16();
486 base::StringPiece data
;
487 if (!locale_resources_data_
->GetStringPiece(static_cast<uint16
>(message_id
),
489 // Fall back on the main data pack (shouldn't be any strings here except in
491 data
= GetRawDataResource(message_id
);
493 NOTREACHED() << "unable to find resource: " << message_id
;
494 return base::string16();
498 // Strings should not be loaded from a data pack that contains binary data.
499 ResourceHandle::TextEncodingType encoding
=
500 locale_resources_data_
->GetTextEncodingType();
501 DCHECK(encoding
== ResourceHandle::UTF16
|| encoding
== ResourceHandle::UTF8
)
502 << "requested localized string from binary pack file";
504 // Data pack encodes strings as either UTF8 or UTF16.
506 if (encoding
== ResourceHandle::UTF16
) {
507 msg
= base::string16(reinterpret_cast<const base::char16
*>(data
.data()),
509 } else if (encoding
== ResourceHandle::UTF8
) {
510 msg
= base::UTF8ToUTF16(data
);
515 const gfx::FontList
& ResourceBundle::GetFontList(FontStyle style
) {
517 base::AutoLock
lock_scope(*images_and_fonts_lock_
);
518 LoadFontsIfNecessary();
522 return *bold_font_list_
;
524 return *small_font_list_
;
526 return *medium_font_list_
;
528 return *small_bold_font_list_
;
530 return *medium_bold_font_list_
;
532 return *large_font_list_
;
534 return *large_bold_font_list_
;
536 return *base_font_list_
;
540 const gfx::Font
& ResourceBundle::GetFont(FontStyle style
) {
541 return GetFontList(style
).GetPrimaryFont();
544 void ResourceBundle::ReloadFonts() {
545 base::AutoLock
lock_scope(*images_and_fonts_lock_
);
546 InitDefaultFontList();
547 base_font_list_
.reset();
548 LoadFontsIfNecessary();
551 ScaleFactor
ResourceBundle::GetMaxScaleFactor() const {
552 #if defined(OS_CHROMEOS) || defined(OS_WIN)
553 return max_scale_factor_
;
555 return GetSupportedScaleFactors().back();
559 bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor
) {
560 const std::vector
<ScaleFactor
>& supported_scale_factors
=
561 ui::GetSupportedScaleFactors();
562 return std::find(supported_scale_factors
.begin(),
563 supported_scale_factors
.end(),
564 scale_factor
) != supported_scale_factors
.end();
567 ResourceBundle::ResourceBundle(Delegate
* delegate
)
568 : delegate_(delegate
),
569 images_and_fonts_lock_(new base::Lock
),
570 locale_resources_data_lock_(new base::Lock
),
571 max_scale_factor_(SCALE_FACTOR_100P
) {
574 ResourceBundle::~ResourceBundle() {
576 UnloadLocaleResources();
580 void ResourceBundle::InitSharedInstance(Delegate
* delegate
) {
581 DCHECK(g_shared_instance_
== NULL
) << "ResourceBundle initialized twice";
582 g_shared_instance_
= new ResourceBundle(delegate
);
583 static std::vector
<ScaleFactor
> supported_scale_factors
;
584 #if !defined(OS_IOS) && !defined(OS_WIN)
585 // On platforms other than iOS, 100P is always a supported scale factor.
586 // For Windows we have a separate case in this function.
587 supported_scale_factors
.push_back(SCALE_FACTOR_100P
);
589 #if defined(OS_ANDROID)
590 const gfx::Display display
=
591 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
592 const float display_density
= display
.device_scale_factor();
593 const ScaleFactor closest
= FindClosestScaleFactorUnsafe(display_density
);
594 if (closest
!= SCALE_FACTOR_100P
)
595 supported_scale_factors
.push_back(closest
);
596 #elif defined(OS_IOS)
597 gfx::Display display
= gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
598 if (display
.device_scale_factor() > 2.0) {
599 DCHECK_EQ(3.0, display
.device_scale_factor());
600 supported_scale_factors
.push_back(SCALE_FACTOR_300P
);
601 } else if (display
.device_scale_factor() > 1.0) {
602 DCHECK_EQ(2.0, display
.device_scale_factor());
603 supported_scale_factors
.push_back(SCALE_FACTOR_200P
);
605 supported_scale_factors
.push_back(SCALE_FACTOR_100P
);
607 #elif defined(OS_MACOSX)
608 if (base::mac::IsOSLionOrLater())
609 supported_scale_factors
.push_back(SCALE_FACTOR_200P
);
610 #elif defined(OS_CHROMEOS)
611 // TODO(oshima): Include 200P only if the device support 200P
612 supported_scale_factors
.push_back(SCALE_FACTOR_200P
);
613 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
614 supported_scale_factors
.push_back(SCALE_FACTOR_200P
);
615 #elif defined(OS_WIN)
616 bool default_to_100P
= true;
617 // On Windows if the dpi scale is greater than 1.25 on high dpi machines
618 // downscaling from 200 percent looks better than scaling up from 100
620 if (gfx::GetDPIScale() > 1.25) {
621 supported_scale_factors
.push_back(SCALE_FACTOR_200P
);
622 default_to_100P
= false;
625 supported_scale_factors
.push_back(SCALE_FACTOR_100P
);
627 ui::SetSupportedScaleFactors(supported_scale_factors
);
629 // Must be called _after_ supported scale factors are set since it
631 gfx::InitDeviceScaleFactor(gfx::GetDPIScale());
635 void ResourceBundle::FreeImages() {
639 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath
& path
,
640 ScaleFactor scale_factor
,
642 // Do not pass an empty |path| value to this method. If the absolute path is
643 // unknown pass just the pack file name.
644 DCHECK(!path
.empty());
646 base::FilePath pack_path
= path
;
648 pack_path
= delegate_
->GetPathForResourcePack(pack_path
, scale_factor
);
650 // Don't try to load empty values or values that are not absolute paths.
651 if (pack_path
.empty() || !pack_path
.IsAbsolute())
654 scoped_ptr
<DataPack
> data_pack(
655 new DataPack(scale_factor
));
656 if (data_pack
->LoadFromPath(pack_path
)) {
657 AddDataPack(data_pack
.release());
658 } else if (!optional
) {
659 LOG(ERROR
) << "Failed to load " << pack_path
.value()
660 << "\nSome features may not be available.";
664 void ResourceBundle::AddDataPack(DataPack
* data_pack
) {
666 data_pack
->CheckForDuplicateResources(data_packs_
);
668 data_packs_
.push_back(data_pack
);
670 if (GetScaleForScaleFactor(data_pack
->GetScaleFactor()) >
671 GetScaleForScaleFactor(max_scale_factor_
))
672 max_scale_factor_
= data_pack
->GetScaleFactor();
675 void ResourceBundle::InitDefaultFontList() {
676 #if defined(OS_CHROMEOS)
677 std::string font_family
= base::UTF16ToUTF8(
678 GetLocalizedString(IDS_UI_FONT_FAMILY_CROS
));
679 gfx::FontList::SetDefaultFontDescription(font_family
);
681 // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to
682 // the font list is done. We will no longer need SetDefaultFontDescription()
683 // after every client gets started using a FontList instead of a Font.
684 gfx::PlatformFontLinux::SetDefaultFontDescription(font_family
);
686 // Use a single default font as the default font list.
687 gfx::FontList::SetDefaultFontDescription(std::string());
691 void ResourceBundle::LoadFontsIfNecessary() {
692 images_and_fonts_lock_
->AssertAcquired();
693 if (!base_font_list_
.get()) {
695 base_font_list_
= GetFontListFromDelegate(BaseFont
);
696 bold_font_list_
= GetFontListFromDelegate(BoldFont
);
697 small_font_list_
= GetFontListFromDelegate(SmallFont
);
698 small_bold_font_list_
= GetFontListFromDelegate(SmallBoldFont
);
699 medium_font_list_
= GetFontListFromDelegate(MediumFont
);
700 medium_bold_font_list_
= GetFontListFromDelegate(MediumBoldFont
);
701 large_font_list_
= GetFontListFromDelegate(LargeFont
);
702 large_bold_font_list_
= GetFontListFromDelegate(LargeBoldFont
);
705 if (!base_font_list_
.get())
706 base_font_list_
.reset(new gfx::FontList());
708 if (!bold_font_list_
.get()) {
709 bold_font_list_
.reset(new gfx::FontList());
710 *bold_font_list_
= base_font_list_
->DeriveWithStyle(
711 base_font_list_
->GetFontStyle() | gfx::Font::BOLD
);
714 if (!small_font_list_
.get()) {
715 small_font_list_
.reset(new gfx::FontList());
717 base_font_list_
->DeriveWithSizeDelta(kSmallFontSizeDelta
);
720 if (!small_bold_font_list_
.get()) {
721 small_bold_font_list_
.reset(new gfx::FontList());
722 *small_bold_font_list_
= small_font_list_
->DeriveWithStyle(
723 small_font_list_
->GetFontStyle() | gfx::Font::BOLD
);
726 if (!medium_font_list_
.get()) {
727 medium_font_list_
.reset(new gfx::FontList());
729 base_font_list_
->DeriveWithSizeDelta(kMediumFontSizeDelta
);
732 if (!medium_bold_font_list_
.get()) {
733 medium_bold_font_list_
.reset(new gfx::FontList());
734 *medium_bold_font_list_
= medium_font_list_
->DeriveWithStyle(
735 medium_font_list_
->GetFontStyle() | gfx::Font::BOLD
);
738 if (!large_font_list_
.get()) {
739 large_font_list_
.reset(new gfx::FontList());
741 base_font_list_
->DeriveWithSizeDelta(kLargeFontSizeDelta
);
744 if (!large_bold_font_list_
.get()) {
745 large_bold_font_list_
.reset(new gfx::FontList());
746 *large_bold_font_list_
= large_font_list_
->DeriveWithStyle(
747 large_font_list_
->GetFontStyle() | gfx::Font::BOLD
);
752 scoped_ptr
<gfx::FontList
> ResourceBundle::GetFontListFromDelegate(
755 scoped_ptr
<gfx::Font
> font
= delegate_
->GetFont(style
);
757 return make_scoped_ptr(new gfx::FontList(*font
));
761 bool ResourceBundle::LoadBitmap(const ResourceHandle
& data_handle
,
764 bool* fell_back_to_1x
) const {
765 DCHECK(fell_back_to_1x
);
766 scoped_refptr
<base::RefCountedMemory
> memory(
767 data_handle
.GetStaticMemory(static_cast<uint16
>(resource_id
)));
771 if (DecodePNG(memory
->front(), memory
->size(), bitmap
, fell_back_to_1x
))
775 // iOS does not compile or use the JPEG codec. On other platforms,
776 // 99% of our assets are PNGs, however fallback to JPEG.
777 scoped_ptr
<SkBitmap
> jpeg_bitmap(
778 gfx::JPEGCodec::Decode(memory
->front(), memory
->size()));
779 if (jpeg_bitmap
.get()) {
780 bitmap
->swap(*jpeg_bitmap
.get());
781 *fell_back_to_1x
= false;
786 NOTREACHED() << "Unable to decode theme image resource " << resource_id
;
790 bool ResourceBundle::LoadBitmap(int resource_id
,
791 ScaleFactor
* scale_factor
,
793 bool* fell_back_to_1x
) const {
794 DCHECK(fell_back_to_1x
);
795 for (size_t i
= 0; i
< data_packs_
.size(); ++i
) {
796 if (data_packs_
[i
]->GetScaleFactor() == ui::SCALE_FACTOR_NONE
&&
797 LoadBitmap(*data_packs_
[i
], resource_id
, bitmap
, fell_back_to_1x
)) {
798 DCHECK(!*fell_back_to_1x
);
799 *scale_factor
= ui::SCALE_FACTOR_NONE
;
802 if (data_packs_
[i
]->GetScaleFactor() == *scale_factor
&&
803 LoadBitmap(*data_packs_
[i
], resource_id
, bitmap
, fell_back_to_1x
)) {
810 gfx::Image
& ResourceBundle::GetEmptyImage() {
811 base::AutoLock
lock(*images_and_fonts_lock_
);
813 if (empty_image_
.IsEmpty()) {
814 // The placeholder bitmap is bright red so people notice the problem.
816 bitmap
.allocN32Pixels(32, 32);
817 bitmap
.eraseARGB(255, 255, 0, 0);
818 empty_image_
= gfx::Image::CreateFrom1xBitmap(bitmap
);
824 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf
,
826 if (size
< arraysize(kPngMagic
) ||
827 memcmp(buf
, kPngMagic
, arraysize(kPngMagic
)) != 0) {
828 // Data invalid or a JPEG.
831 size_t pos
= arraysize(kPngMagic
);
833 // Scan for custom chunks until we find one, find the IDAT chunk, or run out
836 if (size
- pos
< kPngChunkMetadataSize
)
839 base::ReadBigEndian(reinterpret_cast<const char*>(buf
+ pos
), &length
);
840 if (size
- pos
- kPngChunkMetadataSize
< length
)
842 if (length
== 0 && memcmp(buf
+ pos
+ sizeof(uint32
), kPngScaleChunkType
,
843 arraysize(kPngScaleChunkType
)) == 0) {
846 if (memcmp(buf
+ pos
+ sizeof(uint32
), kPngDataChunkType
,
847 arraysize(kPngDataChunkType
)) == 0) {
848 // Stop looking for custom chunks, any custom chunks should be before an
852 pos
+= length
+ kPngChunkMetadataSize
;
858 bool ResourceBundle::DecodePNG(const unsigned char* buf
,
861 bool* fell_back_to_1x
) {
862 *fell_back_to_1x
= PNGContainsFallbackMarker(buf
, size
);
863 return gfx::PNGCodec::Decode(buf
, size
, bitmap
);