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