OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/resource/resource_bundle.h" | 5 #include "ui/base/resource/resource_bundle.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/ref_counted_memory.h" | 12 #include "base/memory/ref_counted_memory.h" |
13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/string_piece.h" | 16 #include "base/string_piece.h" |
17 #include "base/synchronization/lock.h" | 17 #include "base/synchronization/lock.h" |
18 #include "base/utf_string_conversions.h" | 18 #include "base/utf_string_conversions.h" |
19 #include "build/build_config.h" | 19 #include "build/build_config.h" |
20 #include "skia/ext/image_operations.h" | 20 #include "skia/ext/image_operations.h" |
21 #include "third_party/skia/include/core/SkBitmap.h" | 21 #include "third_party/skia/include/core/SkBitmap.h" |
22 #include "ui/base/l10n/l10n_util.h" | 22 #include "ui/base/l10n/l10n_util.h" |
23 #include "ui/base/layout.h" | 23 #include "ui/base/layout.h" |
24 #include "ui/base/resource/data_pack.h" | 24 #include "ui/base/resource/data_pack.h" |
25 #include "ui/base/ui_base_paths.h" | 25 #include "ui/base/ui_base_paths.h" |
26 #include "ui/base/ui_base_switches.h" | 26 #include "ui/base/ui_base_switches.h" |
27 #include "ui/gfx/codec/jpeg_codec.h" | 27 #include "ui/gfx/codec/jpeg_codec.h" |
28 #include "ui/gfx/codec/png_codec.h" | 28 #include "ui/gfx/codec/png_codec.h" |
29 #include "ui/gfx/image/image_skia.h" | 29 #include "ui/gfx/image/image_skia.h" |
| 30 #include "ui/gfx/image/image_skia_source.h" |
30 #include "ui/gfx/screen.h" | 31 #include "ui/gfx/screen.h" |
31 #include "ui/gfx/skbitmap_operations.h" | 32 #include "ui/gfx/skbitmap_operations.h" |
32 | 33 |
33 namespace ui { | 34 namespace ui { |
34 | 35 |
35 namespace { | 36 namespace { |
36 | 37 |
37 // Font sizes relative to base font. | 38 // Font sizes relative to base font. |
38 const int kSmallFontSizeDelta = -2; | 39 const int kSmallFontSizeDelta = -2; |
39 const int kMediumFontSizeDelta = 3; | 40 const int kMediumFontSizeDelta = 3; |
40 const int kLargeFontSizeDelta = 8; | 41 const int kLargeFontSizeDelta = 8; |
41 | 42 |
| 43 ResourceBundle* g_shared_instance_ = NULL; |
| 44 |
42 // Returns the actual scale factor of |bitmap| given the image representations | 45 // Returns the actual scale factor of |bitmap| given the image representations |
43 // which have already been added to |image|. | 46 // which have already been added to |image|. |
44 // TODO(pkotwicz): Remove this once we are no longer loading 1x resources | 47 // TODO(pkotwicz): Remove this once we are no longer loading 1x resources |
45 // as part of 2x data packs. | 48 // as part of 2x data packs. |
46 ui::ScaleFactor GetActualScaleFactor(const gfx::ImageSkia& image, | 49 ui::ScaleFactor GetActualScaleFactor(const gfx::ImageSkia& image, |
47 const SkBitmap& bitmap, | 50 const SkBitmap& bitmap, |
48 ui::ScaleFactor data_pack_scale_factor) { | 51 ui::ScaleFactor data_pack_scale_factor) { |
49 if (image.isNull()) | 52 if (image.isNull()) |
50 return data_pack_scale_factor; | 53 return data_pack_scale_factor; |
51 | 54 |
52 return ui::GetScaleFactorFromScale( | 55 return ui::GetScaleFactorFromScale( |
53 static_cast<float>(bitmap.width()) / image.width()); | 56 static_cast<float>(bitmap.width()) / image.width()); |
54 } | 57 } |
55 | 58 |
56 // If 2x resource is missing from |image| or is the incorrect size, | 59 bool ShouldHighlightMissing2xResources() { |
57 // logs the resource id and creates a 2x version of the resource. | 60 return CommandLine::ForCurrentProcess()->HasSwitch( |
58 // Blends the created resource with red to make it distinguishable from | 61 switches::kHighlightMissing2xResources); |
59 // bitmaps in the resource pak. | |
60 void Create2xResourceIfMissing(gfx::ImageSkia image, int idr) { | |
61 CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
62 if (command_line->HasSwitch( | |
63 switches::kHighlightMissing2xResources) && | |
64 !image.HasRepresentation(ui::SCALE_FACTOR_200P)) { | |
65 gfx::ImageSkiaRep image_rep = image.GetRepresentation(SCALE_FACTOR_200P); | |
66 | |
67 if (image_rep.scale_factor() == ui::SCALE_FACTOR_100P) | |
68 LOG(INFO) << "Missing 2x resource with id " << idr; | |
69 else | |
70 LOG(INFO) << "Incorrectly sized 2x resource with id " << idr; | |
71 | |
72 SkBitmap bitmap2x = skia::ImageOperations::Resize(image_rep.sk_bitmap(), | |
73 skia::ImageOperations::RESIZE_LANCZOS3, | |
74 image.width() * 2, image.height() * 2); | |
75 | |
76 SkBitmap mask; | |
77 mask.setConfig(SkBitmap::kARGB_8888_Config, | |
78 bitmap2x.width(), | |
79 bitmap2x.height()); | |
80 mask.allocPixels(); | |
81 mask.eraseColor(SK_ColorRED); | |
82 SkBitmap result = SkBitmapOperations::CreateBlendedBitmap(bitmap2x, mask, | |
83 0.2); | |
84 image.AddRepresentation(gfx::ImageSkiaRep(result, SCALE_FACTOR_200P)); | |
85 } | |
86 } | 62 } |
87 | 63 |
88 } // namespace | 64 } // namespace |
89 | 65 |
90 ResourceBundle* ResourceBundle::g_shared_instance_ = NULL; | 66 // An ImageSkiaSource that loads bitmaps for given scale factor from |
| 67 // ResourceBundle on demand for given resource_id. It falls back |
| 68 // to 100P image if corresponding 200P image doesn't exist. |
| 69 // If 200P image does not have 2x size of 100P images, it will end up |
| 70 // with broken UI because it will be drawn as if it has 2x size. |
| 71 // When --highlight-missing-2x-resources flag is specified, it |
| 72 // will show the scaled image blended with red instead. |
| 73 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { |
| 74 public: |
| 75 ResourceBundleImageSource(int resource_id, const gfx::Size& size_in_dip) |
| 76 : resource_id_(resource_id), |
| 77 size_in_dip_(size_in_dip) { |
| 78 } |
| 79 virtual ~ResourceBundleImageSource() {} |
| 80 |
| 81 // gfx::ImageSkiaSource overrides: |
| 82 virtual gfx::ImageSkiaRep GetImageForScale(ui::ScaleFactor scale_factor) { |
| 83 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 84 |
| 85 scoped_ptr<SkBitmap> result(rb.LoadBitmap(resource_id_, scale_factor)); |
| 86 gfx::Size size_in_pixel = |
| 87 size_in_dip_.Scale(ui::GetScaleFactorScale(scale_factor)); |
| 88 |
| 89 if (scale_factor == SCALE_FACTOR_200P && |
| 90 (!result.get() || |
| 91 result->width() != size_in_pixel.width() || |
| 92 result->height() != size_in_pixel.height())) { |
| 93 |
| 94 // If 2x resource is missing from |image| or is the incorrect |
| 95 // size and --highlight-missing-2x-resources is specified, logs |
| 96 // the resource id and creates a 2x version of the resource. |
| 97 // Blends the created resource with red to make it |
| 98 // distinguishable from bitmaps in the resource pak. |
| 99 if (ShouldHighlightMissing2xResources()) { |
| 100 if (!result.get()) |
| 101 LOG(ERROR) << "Missing 2x resource. id=" << resource_id_; |
| 102 else |
| 103 LOG(ERROR) << "Incorrectly sized 2x resource. id=" << resource_id_; |
| 104 |
| 105 SkBitmap bitmap1x = *(rb.LoadBitmap(resource_id_, SCALE_FACTOR_100P)); |
| 106 SkBitmap bitmap2x = skia::ImageOperations::Resize( |
| 107 bitmap1x, |
| 108 skia::ImageOperations::RESIZE_LANCZOS3, |
| 109 bitmap1x.width() * 2, bitmap1x.height() * 2); |
| 110 |
| 111 SkBitmap mask; |
| 112 mask.setConfig(SkBitmap::kARGB_8888_Config, |
| 113 bitmap2x.width(), |
| 114 bitmap2x.height()); |
| 115 mask.allocPixels(); |
| 116 mask.eraseColor(SK_ColorRED); |
| 117 result.reset(new SkBitmap()); |
| 118 *result.get() = SkBitmapOperations::CreateBlendedBitmap(bitmap2x, mask, |
| 119 0.2); |
| 120 } else if (!result.get() || |
| 121 result->width() == size_in_dip_.width()) { |
| 122 // The 2x resource pack may have the 1x image if its grd file |
| 123 // points to 1x image. Fallback to 1x by returning empty image |
| 124 // in this case. This 1x image will be scaled when drawn. |
| 125 return gfx::ImageSkiaRep(); |
| 126 } |
| 127 // If the size of 2x image isn't exactly 2x of 1x version, |
| 128 // create ImageSkia as usual. This will end up with |
| 129 // corrupted visual representation as the size of image doesn't |
| 130 // match the expected size. |
| 131 } |
| 132 return gfx::ImageSkiaRep(*result.get(), scale_factor); |
| 133 } |
| 134 |
| 135 private: |
| 136 const int resource_id_; |
| 137 const gfx::Size size_in_dip_; |
| 138 |
| 139 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); |
| 140 }; |
91 | 141 |
92 // static | 142 // static |
93 std::string ResourceBundle::InitSharedInstanceWithLocale( | 143 std::string ResourceBundle::InitSharedInstanceWithLocale( |
94 const std::string& pref_locale, Delegate* delegate) { | 144 const std::string& pref_locale, Delegate* delegate) { |
95 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; | 145 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; |
96 g_shared_instance_ = new ResourceBundle(delegate); | 146 g_shared_instance_ = new ResourceBundle(delegate); |
97 | 147 |
98 g_shared_instance_->LoadCommonResources(); | 148 g_shared_instance_->LoadCommonResources(); |
99 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale); | 149 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale); |
100 return result; | 150 return result; |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 return images_[resource_id]; | 354 return images_[resource_id]; |
305 } | 355 } |
306 | 356 |
307 gfx::Image image; | 357 gfx::Image image; |
308 if (delegate_) | 358 if (delegate_) |
309 image = delegate_->GetImageNamed(resource_id); | 359 image = delegate_->GetImageNamed(resource_id); |
310 | 360 |
311 if (image.IsEmpty()) { | 361 if (image.IsEmpty()) { |
312 DCHECK(!delegate_ && !data_packs_.empty()) << | 362 DCHECK(!delegate_ && !data_packs_.empty()) << |
313 "Missing call to SetResourcesDataDLL?"; | 363 "Missing call to SetResourcesDataDLL?"; |
314 gfx::ImageSkia image_skia; | |
315 for (size_t i = 0; i < data_packs_.size(); ++i) { | |
316 scoped_ptr<SkBitmap> bitmap(LoadBitmap(*data_packs_[i], resource_id)); | |
317 if (bitmap.get()) { | |
318 ui::ScaleFactor scale_factor; | |
319 if (gfx::Screen::IsDIPEnabled()) { | |
320 scale_factor = GetActualScaleFactor(image_skia, *bitmap, | |
321 data_packs_[i]->GetScaleFactor()); | |
322 } else { | |
323 scale_factor = ui::SCALE_FACTOR_100P; | |
324 } | |
325 image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap, scale_factor)); | |
326 } | |
327 } | |
328 | 364 |
329 if (image_skia.isNull()) { | 365 // TODO(oshima): Pick the scale factor from currently used scale factors. |
| 366 scoped_ptr<SkBitmap> bitmap(LoadBitmap(resource_id, SCALE_FACTOR_100P)); |
| 367 if (!bitmap.get()) { |
330 LOG(WARNING) << "Unable to load image with id " << resource_id; | 368 LOG(WARNING) << "Unable to load image with id " << resource_id; |
331 NOTREACHED(); // Want to assert in debug mode. | 369 NOTREACHED(); // Want to assert in debug mode. |
332 // The load failed to retrieve the image; show a debugging red square. | 370 // The load failed to retrieve the image; show a debugging red square. |
333 return GetEmptyImage(); | 371 return GetEmptyImage(); |
334 } | 372 } |
335 | 373 |
336 Create2xResourceIfMissing(image_skia, resource_id); | 374 gfx::Size size_in_dip(bitmap->width(), bitmap->height()); |
337 | 375 gfx::ImageSkia image_skia( |
| 376 new ResourceBundleImageSource(resource_id, size_in_dip), |
| 377 size_in_dip); |
| 378 image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap.get(), |
| 379 SCALE_FACTOR_100P)); |
| 380 image_skia.SetReadOnly(); |
338 image = gfx::Image(image_skia); | 381 image = gfx::Image(image_skia); |
339 } | 382 } |
340 | 383 |
341 // The load was successful, so cache the image. | 384 // The load was successful, so cache the image. |
342 base::AutoLock lock_scope(*images_and_fonts_lock_); | 385 base::AutoLock lock_scope(*images_and_fonts_lock_); |
343 | 386 |
344 // Another thread raced the load and has already cached the image. | 387 // Another thread raced the load and has already cached the image. |
345 if (images_.count(resource_id)) | 388 if (images_.count(resource_id)) |
346 return images_[resource_id]; | 389 return images_[resource_id]; |
347 | 390 |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 if (!large_bold_font_.get()) { | 573 if (!large_bold_font_.get()) { |
531 large_bold_font_.reset(new gfx::Font()); | 574 large_bold_font_.reset(new gfx::Font()); |
532 *large_bold_font_ = | 575 *large_bold_font_ = |
533 base_font_->DeriveFont(kLargeFontSizeDelta, | 576 base_font_->DeriveFont(kLargeFontSizeDelta, |
534 base_font_->GetStyle() | gfx::Font::BOLD); | 577 base_font_->GetStyle() | gfx::Font::BOLD); |
535 } | 578 } |
536 } | 579 } |
537 } | 580 } |
538 | 581 |
539 SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, | 582 SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, |
540 int resource_id) { | 583 int resource_id) const { |
541 scoped_refptr<base::RefCountedMemory> memory( | 584 scoped_refptr<base::RefCountedMemory> memory( |
542 data_handle.GetStaticMemory(resource_id)); | 585 data_handle.GetStaticMemory(resource_id)); |
543 if (!memory) | 586 if (!memory) |
544 return NULL; | 587 return NULL; |
545 | 588 |
546 SkBitmap bitmap; | 589 SkBitmap bitmap; |
547 if (gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) | 590 if (gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) |
548 return new SkBitmap(bitmap); | 591 return new SkBitmap(bitmap); |
549 | 592 |
550 // 99% of our assets are PNGs, however fallback to JPEG. | 593 // 99% of our assets are PNGs, however fallback to JPEG. |
551 SkBitmap* allocated_bitmap = | 594 SkBitmap* allocated_bitmap = |
552 gfx::JPEGCodec::Decode(memory->front(), memory->size()); | 595 gfx::JPEGCodec::Decode(memory->front(), memory->size()); |
553 if (allocated_bitmap) | 596 if (allocated_bitmap) |
554 return allocated_bitmap; | 597 return allocated_bitmap; |
555 | 598 |
556 NOTREACHED() << "Unable to decode theme image resource " << resource_id; | 599 NOTREACHED() << "Unable to decode theme image resource " << resource_id; |
557 return NULL; | 600 return NULL; |
558 } | 601 } |
559 | 602 |
| 603 SkBitmap* ResourceBundle::LoadBitmap(int resource_id, |
| 604 ScaleFactor scale_factor) const { |
| 605 for (size_t i = 0; i < data_packs_.size(); ++i) { |
| 606 if (data_packs_[i]->GetScaleFactor() == scale_factor) { |
| 607 SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); |
| 608 if (bitmap) |
| 609 return bitmap; |
| 610 } |
| 611 } |
| 612 return NULL; |
| 613 } |
| 614 |
560 gfx::Image& ResourceBundle::GetEmptyImage() { | 615 gfx::Image& ResourceBundle::GetEmptyImage() { |
561 base::AutoLock lock(*images_and_fonts_lock_); | 616 base::AutoLock lock(*images_and_fonts_lock_); |
562 | 617 |
563 if (empty_image_.IsEmpty()) { | 618 if (empty_image_.IsEmpty()) { |
564 // The placeholder bitmap is bright red so people notice the problem. | 619 // The placeholder bitmap is bright red so people notice the problem. |
565 SkBitmap bitmap; | 620 SkBitmap bitmap; |
566 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); | 621 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); |
567 bitmap.allocPixels(); | 622 bitmap.allocPixels(); |
568 bitmap.eraseARGB(255, 255, 0, 0); | 623 bitmap.eraseARGB(255, 255, 0, 0); |
569 empty_image_ = gfx::Image(bitmap); | 624 empty_image_ = gfx::Image(bitmap); |
570 } | 625 } |
571 return empty_image_; | 626 return empty_image_; |
572 } | 627 } |
573 | 628 |
574 } // namespace ui | 629 } // namespace ui |
OLD | NEW |