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"
9 #include "base/big_endian.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/files/file.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/metrics/histogram.h"
16 #include "base/path_service.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/synchronization/lock.h"
21 #include "build/build_config.h"
22 #include "grit/app_locale_settings.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"
38 #if defined(OS_CHROMEOS)
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/gfx/platform_font_pango.h"
44 #include "ui/base/win/dpi_setup.h"
45 #include "ui/gfx/win/dpi.h"
48 #if defined(OS_MACOSX) && !defined(OS_IOS)
49 #include "base/mac/mac_util.h"
56 // Font sizes relative to base font.
57 const int kSmallFontSizeDelta = -1;
58 const int kMediumFontSizeDelta = 3;
59 const int kLargeFontSizeDelta = 8;
61 // PNG-related constants.
62 const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
63 const size_t kPngChunkMetadataSize = 12; // length, type, crc32
64 const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' };
65 const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' };
67 ResourceBundle* g_shared_instance_ = NULL;
69 void InitDefaultFontList() {
70 #if defined(OS_CHROMEOS)
71 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
72 std::string font_family = base::UTF16ToUTF8(
73 rb.GetLocalizedString(IDS_UI_FONT_FAMILY_CROS));
74 gfx::FontList::SetDefaultFontDescription(font_family);
76 // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to
77 // the font list is done. We will no longer need SetDefaultFontDescription()
78 // after every client gets started using a FontList instead of a Font.
79 gfx::PlatformFontPango::SetDefaultFontDescription(font_family);
81 // Use a single default font as the default font list.
82 gfx::FontList::SetDefaultFontDescription(std::string());
88 // An ImageSkiaSource that loads bitmaps for the requested scale factor from
89 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the
90 // requested scale factor does not exist, it will return the 1x bitmap scaled
91 // by the scale factor. This may lead to broken UI if the correct size of the
92 // scaled image is not exactly |scale_factor| * the size of the 1x resource.
93 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images
94 // are higlighted by blending them with red.
95 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource {
97 ResourceBundleImageSource(ResourceBundle* rb, int resource_id)
98 : rb_(rb), resource_id_(resource_id) {}
99 virtual ~ResourceBundleImageSource() {}
101 // gfx::ImageSkiaSource overrides:
102 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
104 bool fell_back_to_1x = false;
105 ScaleFactor scale_factor = GetSupportedScaleFactor(scale);
106 bool found = rb_->LoadBitmap(resource_id_, &scale_factor,
107 &image, &fell_back_to_1x);
109 return gfx::ImageSkiaRep();
111 if (fell_back_to_1x) {
112 // GRIT fell back to the 100% image, so rescale it to the correct size.
113 image = skia::ImageOperations::Resize(
115 skia::ImageOperations::RESIZE_LANCZOS3,
116 gfx::ToCeiledInt(image.width() * scale),
117 gfx::ToCeiledInt(image.height() * scale));
119 image = PlatformScaleImage(image,
120 ui::GetScaleForScaleFactor(scale_factor),
123 return gfx::ImageSkiaRep(image, scale);
128 const int resource_id_;
130 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource);
134 std::string ResourceBundle::InitSharedInstanceWithLocale(
135 const std::string& pref_locale, Delegate* delegate) {
136 InitSharedInstance(delegate);
137 g_shared_instance_->LoadCommonResources();
138 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
139 InitDefaultFontList();
144 std::string ResourceBundle::InitSharedInstanceLocaleOnly(
145 const std::string& pref_locale, Delegate* delegate) {
146 InitSharedInstance(delegate);
147 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
148 InitDefaultFontList();
153 void ResourceBundle::InitSharedInstanceWithPakFile(
154 base::File pak_file, bool should_load_common_resources) {
155 InitSharedInstance(NULL);
156 if (should_load_common_resources)
157 g_shared_instance_->LoadCommonResources();
159 scoped_ptr<DataPack> data_pack(
160 new DataPack(SCALE_FACTOR_100P));
161 if (!data_pack->LoadFromFile(pak_file.Pass())) {
162 NOTREACHED() << "failed to load pak file";
165 g_shared_instance_->locale_resources_data_.reset(data_pack.release());
166 InitDefaultFontList();
170 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) {
171 InitSharedInstance(NULL);
172 g_shared_instance_->LoadTestResources(path, path);
174 InitDefaultFontList();
178 void ResourceBundle::CleanupSharedInstance() {
179 if (g_shared_instance_) {
180 delete g_shared_instance_;
181 g_shared_instance_ = NULL;
186 bool ResourceBundle::HasSharedInstance() {
187 return g_shared_instance_ != NULL;
191 ResourceBundle& ResourceBundle::GetSharedInstance() {
192 // Must call InitSharedInstance before this function.
193 CHECK(g_shared_instance_ != NULL);
194 return *g_shared_instance_;
197 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
198 return !GetLocaleFilePath(locale, true).empty();
201 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
202 ScaleFactor scale_factor) {
203 AddDataPackFromPathInternal(path, scale_factor, false);
206 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path,
207 ScaleFactor scale_factor) {
208 AddDataPackFromPathInternal(path, scale_factor, true);
211 void ResourceBundle::AddDataPackFromFile(base::File file,
212 ScaleFactor scale_factor) {
213 scoped_ptr<DataPack> data_pack(
214 new DataPack(scale_factor));
215 if (data_pack->LoadFromFile(file.Pass())) {
216 AddDataPack(data_pack.release());
218 LOG(ERROR) << "Failed to load data pack from file."
219 << "\nSome features may not be available.";
223 #if !defined(OS_MACOSX)
224 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale,
225 bool test_file_exists) {
226 if (app_locale.empty())
227 return base::FilePath();
229 base::FilePath locale_file_path;
231 PathService::Get(ui::DIR_LOCALES, &locale_file_path);
233 if (!locale_file_path.empty())
234 locale_file_path = locale_file_path.AppendASCII(app_locale + ".pak");
238 delegate_->GetPathForLocalePack(locale_file_path, app_locale);
241 // Don't try to load empty values or values that are not absolute paths.
242 if (locale_file_path.empty() || !locale_file_path.IsAbsolute())
243 return base::FilePath();
245 if (test_file_exists && !base::PathExists(locale_file_path))
246 return base::FilePath();
248 return locale_file_path;
252 std::string ResourceBundle::LoadLocaleResources(
253 const std::string& pref_locale) {
254 DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded";
255 std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);
256 base::FilePath locale_file_path = GetOverriddenPakPath();
257 if (locale_file_path.empty()) {
258 CommandLine* command_line = CommandLine::ForCurrentProcess();
259 if (command_line->HasSwitch(switches::kLocalePak)) {
261 command_line->GetSwitchValuePath(switches::kLocalePak);
263 locale_file_path = GetLocaleFilePath(app_locale, true);
267 if (locale_file_path.empty()) {
268 // It's possible that there is no locale.pak.
269 LOG(WARNING) << "locale_file_path.empty()";
270 return std::string();
273 scoped_ptr<DataPack> data_pack(
274 new DataPack(SCALE_FACTOR_100P));
275 if (!data_pack->LoadFromPath(locale_file_path)) {
276 UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError",
277 logging::GetLastSystemErrorCode(), 16000);
278 LOG(ERROR) << "failed to load locale.pak";
280 return std::string();
283 locale_resources_data_.reset(data_pack.release());
287 void ResourceBundle::LoadTestResources(const base::FilePath& path,
288 const base::FilePath& locale_path) {
289 // Use the given resource pak for both common and localized resources.
290 scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P));
291 if (!path.empty() && data_pack->LoadFromPath(path))
292 AddDataPack(data_pack.release());
294 data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE));
295 if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
296 locale_resources_data_.reset(data_pack.release());
298 locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE));
302 void ResourceBundle::UnloadLocaleResources() {
303 locale_resources_data_.reset();
306 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) {
307 overridden_pak_path_ = pak_path;
310 const base::FilePath& ResourceBundle::GetOverriddenPakPath() {
311 return overridden_pak_path_;
314 std::string ResourceBundle::ReloadLocaleResources(
315 const std::string& pref_locale) {
316 base::AutoLock lock_scope(*locale_resources_data_lock_);
317 UnloadLocaleResources();
318 return LoadLocaleResources(pref_locale);
321 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) {
322 const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia();
323 return const_cast<gfx::ImageSkia*>(image);
326 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) {
327 // Check to see if the image is already in the cache.
329 base::AutoLock lock_scope(*images_and_fonts_lock_);
330 if (images_.count(resource_id))
331 return images_[resource_id];
336 image = delegate_->GetImageNamed(resource_id);
338 if (image.IsEmpty()) {
339 DCHECK(!data_packs_.empty()) <<
340 "Missing call to SetResourcesDataDLL?";
342 #if defined(OS_CHROMEOS) || defined(OS_WIN)
343 ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor();
345 ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
348 float scale = GetImageScale(scale_factor_to_load);
350 // TODO(oshima): Consider reading the image size from png IHDR chunk and
351 // skip decoding here and remove #ifdef below.
352 // ResourceBundle::GetSharedInstance() is destroyed after the
353 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be
354 // destroyed before the resource bundle is destroyed.
355 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id),
357 if (image_skia.isNull()) {
358 LOG(WARNING) << "Unable to load image with id " << resource_id;
359 NOTREACHED(); // Want to assert in debug mode.
360 // The load failed to retrieve the image; show a debugging red square.
361 return GetEmptyImage();
363 image_skia.SetReadOnly();
364 image = gfx::Image(image_skia);
367 // The load was successful, so cache the image.
368 base::AutoLock lock_scope(*images_and_fonts_lock_);
370 // Another thread raced the load and has already cached the image.
371 if (images_.count(resource_id))
372 return images_[resource_id];
374 images_[resource_id] = image;
375 return images_[resource_id];
378 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
379 return GetNativeImageNamed(resource_id, RTL_DISABLED);
382 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes(
383 int resource_id) const {
384 return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE);
387 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale(
389 ScaleFactor scale_factor) const {
390 base::RefCountedStaticMemory* bytes = NULL;
392 bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor);
395 base::StringPiece data =
396 GetRawDataResourceForScale(resource_id, scale_factor);
398 bytes = new base::RefCountedStaticMemory(data.data(), data.length());
405 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const {
406 return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE);
409 base::StringPiece ResourceBundle::GetRawDataResourceForScale(
411 ScaleFactor scale_factor) const {
412 base::StringPiece data;
414 delegate_->GetRawDataResource(resource_id, scale_factor, &data))
417 if (scale_factor != ui::SCALE_FACTOR_100P) {
418 for (size_t i = 0; i < data_packs_.size(); i++) {
419 if (data_packs_[i]->GetScaleFactor() == scale_factor &&
420 data_packs_[i]->GetStringPiece(resource_id, &data))
424 for (size_t i = 0; i < data_packs_.size(); i++) {
425 if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P ||
426 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P ||
427 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) &&
428 data_packs_[i]->GetStringPiece(resource_id, &data))
432 return base::StringPiece();
435 base::string16 ResourceBundle::GetLocalizedString(int message_id) {
436 base::string16 string;
437 if (delegate_ && delegate_->GetLocalizedString(message_id, &string))
440 // Ensure that ReloadLocaleResources() doesn't drop the resources while
442 base::AutoLock lock_scope(*locale_resources_data_lock_);
444 // If for some reason we were unable to load the resources , return an empty
445 // string (better than crashing).
446 if (!locale_resources_data_.get()) {
447 LOG(WARNING) << "locale resources are not loaded";
448 return base::string16();
451 base::StringPiece data;
452 if (!locale_resources_data_->GetStringPiece(message_id, &data)) {
453 // Fall back on the main data pack (shouldn't be any strings here except in
455 data = GetRawDataResource(message_id);
457 NOTREACHED() << "unable to find resource: " << message_id;
458 return base::string16();
462 // Strings should not be loaded from a data pack that contains binary data.
463 ResourceHandle::TextEncodingType encoding =
464 locale_resources_data_->GetTextEncodingType();
465 DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8)
466 << "requested localized string from binary pack file";
468 // Data pack encodes strings as either UTF8 or UTF16.
470 if (encoding == ResourceHandle::UTF16) {
471 msg = base::string16(reinterpret_cast<const base::char16*>(data.data()),
473 } else if (encoding == ResourceHandle::UTF8) {
474 msg = base::UTF8ToUTF16(data);
479 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) {
481 base::AutoLock lock_scope(*images_and_fonts_lock_);
482 LoadFontsIfNecessary();
486 return *bold_font_list_;
488 return *small_font_list_;
490 return *medium_font_list_;
492 return *small_bold_font_list_;
494 return *medium_bold_font_list_;
496 return *large_font_list_;
498 return *large_bold_font_list_;
500 return *base_font_list_;
504 const gfx::Font& ResourceBundle::GetFont(FontStyle style) {
505 return GetFontList(style).GetPrimaryFont();
508 void ResourceBundle::ReloadFonts() {
509 base::AutoLock lock_scope(*images_and_fonts_lock_);
510 base_font_list_.reset();
511 LoadFontsIfNecessary();
514 ScaleFactor ResourceBundle::GetMaxScaleFactor() const {
515 #if defined(OS_CHROMEOS) || defined(OS_WIN)
516 return max_scale_factor_;
518 return GetSupportedScaleFactors().back();
522 ResourceBundle::ResourceBundle(Delegate* delegate)
523 : delegate_(delegate),
524 images_and_fonts_lock_(new base::Lock),
525 locale_resources_data_lock_(new base::Lock),
526 max_scale_factor_(SCALE_FACTOR_100P) {
529 ResourceBundle::~ResourceBundle() {
531 UnloadLocaleResources();
535 void ResourceBundle::InitSharedInstance(Delegate* delegate) {
536 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
537 g_shared_instance_ = new ResourceBundle(delegate);
538 static std::vector<ScaleFactor> supported_scale_factors;
539 #if !defined(OS_IOS) && !defined(OS_WIN)
540 // On platforms other than iOS, 100P is always a supported scale factor.
541 // For Windows we have a separate case in this function.
542 supported_scale_factors.push_back(SCALE_FACTOR_100P);
544 #if defined(OS_ANDROID)
545 const gfx::Display display =
546 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
547 const float display_density = display.device_scale_factor();
548 const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density);
549 if (closest != SCALE_FACTOR_100P)
550 supported_scale_factors.push_back(closest);
551 #elif defined(OS_IOS)
552 gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
553 if (display.device_scale_factor() > 1.0) {
554 DCHECK_EQ(2.0, display.device_scale_factor());
555 supported_scale_factors.push_back(SCALE_FACTOR_200P);
557 supported_scale_factors.push_back(SCALE_FACTOR_100P);
559 #elif defined(OS_MACOSX)
560 if (base::mac::IsOSLionOrLater())
561 supported_scale_factors.push_back(SCALE_FACTOR_200P);
562 #elif defined(OS_CHROMEOS)
563 // TODO(oshima): Include 200P only if the device support 200P
564 supported_scale_factors.push_back(SCALE_FACTOR_200P);
565 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
566 supported_scale_factors.push_back(SCALE_FACTOR_200P);
567 #elif defined(OS_WIN)
568 bool default_to_100P = true;
569 if (gfx::IsHighDPIEnabled()) {
570 // On Windows if the dpi scale is greater than 1.25 on high dpi machines
571 // downscaling from 200 percent looks better than scaling up from 100
573 if (gfx::GetDPIScale() > 1.25) {
574 supported_scale_factors.push_back(SCALE_FACTOR_200P);
575 default_to_100P = false;
579 supported_scale_factors.push_back(SCALE_FACTOR_100P);
581 ui::SetSupportedScaleFactors(supported_scale_factors);
583 // Must be called _after_ supported scale factors are set since it
585 ui::win::InitDeviceScaleFactor();
589 void ResourceBundle::FreeImages() {
593 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path,
594 ScaleFactor scale_factor,
596 // Do not pass an empty |path| value to this method. If the absolute path is
597 // unknown pass just the pack file name.
598 DCHECK(!path.empty());
600 base::FilePath pack_path = path;
602 pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor);
604 // Don't try to load empty values or values that are not absolute paths.
605 if (pack_path.empty() || !pack_path.IsAbsolute())
608 scoped_ptr<DataPack> data_pack(
609 new DataPack(scale_factor));
610 if (data_pack->LoadFromPath(pack_path)) {
611 AddDataPack(data_pack.release());
612 } else if (!optional) {
613 LOG(ERROR) << "Failed to load " << pack_path.value()
614 << "\nSome features may not be available.";
618 void ResourceBundle::AddDataPack(DataPack* data_pack) {
619 data_packs_.push_back(data_pack);
621 if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
622 GetScaleForScaleFactor(max_scale_factor_))
623 max_scale_factor_ = data_pack->GetScaleFactor();
626 void ResourceBundle::LoadFontsIfNecessary() {
627 images_and_fonts_lock_->AssertAcquired();
628 if (!base_font_list_.get()) {
630 base_font_list_ = GetFontListFromDelegate(BaseFont);
631 bold_font_list_ = GetFontListFromDelegate(BoldFont);
632 small_font_list_ = GetFontListFromDelegate(SmallFont);
633 small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont);
634 medium_font_list_ = GetFontListFromDelegate(MediumFont);
635 medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont);
636 large_font_list_ = GetFontListFromDelegate(LargeFont);
637 large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
640 if (!base_font_list_.get())
641 base_font_list_.reset(new gfx::FontList());
643 if (!bold_font_list_.get()) {
644 bold_font_list_.reset(new gfx::FontList());
645 *bold_font_list_ = base_font_list_->DeriveWithStyle(
646 base_font_list_->GetFontStyle() | gfx::Font::BOLD);
649 if (!small_font_list_.get()) {
650 small_font_list_.reset(new gfx::FontList());
652 base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta);
655 if (!small_bold_font_list_.get()) {
656 small_bold_font_list_.reset(new gfx::FontList());
657 *small_bold_font_list_ = small_font_list_->DeriveWithStyle(
658 small_font_list_->GetFontStyle() | gfx::Font::BOLD);
661 if (!medium_font_list_.get()) {
662 medium_font_list_.reset(new gfx::FontList());
664 base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta);
667 if (!medium_bold_font_list_.get()) {
668 medium_bold_font_list_.reset(new gfx::FontList());
669 *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle(
670 medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
673 if (!large_font_list_.get()) {
674 large_font_list_.reset(new gfx::FontList());
676 base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta);
679 if (!large_bold_font_list_.get()) {
680 large_bold_font_list_.reset(new gfx::FontList());
681 *large_bold_font_list_ = large_font_list_->DeriveWithStyle(
682 large_font_list_->GetFontStyle() | gfx::Font::BOLD);
687 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate(
690 scoped_ptr<gfx::Font> font = delegate_->GetFont(style);
692 return scoped_ptr<gfx::FontList>(new gfx::FontList(*font));
693 return scoped_ptr<gfx::FontList>();
696 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle,
699 bool* fell_back_to_1x) const {
700 DCHECK(fell_back_to_1x);
701 scoped_refptr<base::RefCountedMemory> memory(
702 data_handle.GetStaticMemory(resource_id));
706 if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x))
710 // iOS does not compile or use the JPEG codec. On other platforms,
711 // 99% of our assets are PNGs, however fallback to JPEG.
712 scoped_ptr<SkBitmap> jpeg_bitmap(
713 gfx::JPEGCodec::Decode(memory->front(), memory->size()));
714 if (jpeg_bitmap.get()) {
715 bitmap->swap(*jpeg_bitmap.get());
716 *fell_back_to_1x = false;
721 NOTREACHED() << "Unable to decode theme image resource " << resource_id;
725 bool ResourceBundle::LoadBitmap(int resource_id,
726 ScaleFactor* scale_factor,
728 bool* fell_back_to_1x) const {
729 DCHECK(fell_back_to_1x);
730 for (size_t i = 0; i < data_packs_.size(); ++i) {
731 // If the resource is in the package with SCALE_FACTOR_NONE, it
732 // can be used in any scale factor, but set 100P in ImageSkia so
733 // that it will be scaled property.
734 if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
735 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
736 *scale_factor = ui::SCALE_FACTOR_100P;
737 DCHECK(!*fell_back_to_1x);
740 if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
741 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
748 gfx::Image& ResourceBundle::GetEmptyImage() {
749 base::AutoLock lock(*images_and_fonts_lock_);
751 if (empty_image_.IsEmpty()) {
752 // The placeholder bitmap is bright red so people notice the problem.
754 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32);
755 bitmap.allocPixels();
756 bitmap.eraseARGB(255, 255, 0, 0);
757 empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
763 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
765 if (size < arraysize(kPngMagic) ||
766 memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
767 // Data invalid or a JPEG.
770 size_t pos = arraysize(kPngMagic);
772 // Scan for custom chunks until we find one, find the IDAT chunk, or run out
775 if (size - pos < kPngChunkMetadataSize)
778 base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
779 if (size - pos - kPngChunkMetadataSize < length)
781 if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,
782 arraysize(kPngScaleChunkType)) == 0) {
785 if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType,
786 arraysize(kPngDataChunkType)) == 0) {
787 // Stop looking for custom chunks, any custom chunks should be before an
791 pos += length + kPngChunkMetadataSize;
797 bool ResourceBundle::DecodePNG(const unsigned char* buf,
800 bool* fell_back_to_1x) {
801 *fell_back_to_1x = PNGContainsFallbackMarker(buf, size);
802 return gfx::PNGCodec::Decode(buf, size, bitmap);
807 SkBitmap ResourceBundle::PlatformScaleImage(const SkBitmap& image,
808 float loaded_image_scale,
809 float desired_scale) {