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