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" |
(...skipping 30 matching lines...) Expand all Loading... | |
41 const int kMediumFontSizeDelta = 3; | 41 const int kMediumFontSizeDelta = 3; |
42 const int kLargeFontSizeDelta = 8; | 42 const int kLargeFontSizeDelta = 8; |
43 | 43 |
44 ResourceBundle* g_shared_instance_ = NULL; | 44 ResourceBundle* g_shared_instance_ = NULL; |
45 | 45 |
46 bool ShouldHighlightMissingScaledResources() { | 46 bool ShouldHighlightMissingScaledResources() { |
47 return CommandLine::ForCurrentProcess()->HasSwitch( | 47 return CommandLine::ForCurrentProcess()->HasSwitch( |
48 switches::kHighlightMissingScaledResources); | 48 switches::kHighlightMissingScaledResources); |
49 } | 49 } |
50 | 50 |
51 // Round a float to an integer. This is not suitable for general use | |
52 // (it fails for negative f and f = 2^23 + 1, for example), but it works for | |
53 // the arguments passed here. | |
54 int RoundToInt(float f) { | |
55 return static_cast<int>(f + 0.5f); | |
56 } | |
57 | |
51 } // namespace | 58 } // namespace |
52 | 59 |
53 // An ImageSkiaSource that loads bitmaps for requested scale factor from | 60 // An ImageSkiaSource that loads bitmaps for the requested scale factor from |
54 // ResourceBundle on demand for given resource_id. It falls back | 61 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the |
55 // to the 1x bitmap if the bitmap for the requested scale factor does not | 62 // requested scale factor does not exist, it will return the 1x bitmap scaled |
56 // exist. If the resource for the requested scale factor is not exactly | 63 // by the scale factor. This may lead to broken UI if the correct size of the |
57 // |scale_factor| * the size of the 1x resource, it will end up with | 64 // scaled image is not exactly |scale_factor| * the size of the 1x resource. |
58 // broken UI because it will be drawn as if the bitmap was the correct size. | 65 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images |
59 // When --highlight-missing-scaled-resources flag is specified, it | 66 // are higlighted by blending them with red. |
60 // will show the scaled image blended with red instead. | |
61 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { | 67 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { |
62 public: | 68 public: |
63 ResourceBundleImageSource(ResourceBundle* rb, | 69 ResourceBundleImageSource(ResourceBundle* rb, int resource_id) |
64 int resource_id, | 70 : rb_(rb), resource_id_(resource_id) {} |
65 const gfx::Size& size_in_dip) | |
66 : rb_(rb), | |
67 resource_id_(resource_id), | |
68 size_in_dip_(size_in_dip) { | |
69 } | |
70 virtual ~ResourceBundleImageSource() {} | 71 virtual ~ResourceBundleImageSource() {} |
71 | 72 |
72 // gfx::ImageSkiaSource overrides: | 73 // gfx::ImageSkiaSource overrides: |
73 virtual gfx::ImageSkiaRep GetImageForScale( | 74 virtual gfx::ImageSkiaRep GetImageForScale( |
74 ui::ScaleFactor scale_factor) OVERRIDE { | 75 ui::ScaleFactor scale_factor) OVERRIDE { |
75 scoped_ptr<SkBitmap> result(rb_->LoadBitmap(resource_id_, scale_factor)); | 76 SkBitmap image; |
76 float scale = ui::GetScaleFactorScale(scale_factor); | 77 bool fell_back_to_1x = false; |
77 gfx::Size size_in_pixel = gfx::ToFlooredSize(size_in_dip_.Scale(scale)); | 78 bool found = rb_->LoadBitmap(resource_id_, scale_factor, |
79 &image, &fell_back_to_1x); | |
78 | 80 |
79 if (scale_factor != SCALE_FACTOR_100P && | 81 if (!found) { |
80 (!result.get() || | 82 // Some images (e.g. wallpapers) exist only at 100%. Return the unscaled |
81 result->width() != size_in_pixel.width() || | 83 // image with the correct scale factor. |
82 result->height() != size_in_pixel.height())) { | 84 if (scale_factor != SCALE_FACTOR_100P) |
85 found = rb_->LoadBitmap(resource_id_, SCALE_FACTOR_100P, &image, NULL); | |
86 if (!found) | |
87 return gfx::ImageSkiaRep(); | |
88 return gfx::ImageSkiaRep(image, SCALE_FACTOR_100P); | |
89 } | |
83 | 90 |
84 // If non 1x resource is missing from |image| or is the incorrect | 91 if (fell_back_to_1x) { |
85 // size and --highlight-missing-scaled-resources is specified, logs | 92 // GRIT fell back to the 100% image, so rescale it to the correct size. |
86 // the resource id and creates a version of the resource at the correct | 93 float scale = GetScaleFactorScale(scale_factor); |
87 // size. Blends the created resource with red to make it | 94 image = skia::ImageOperations::Resize( |
88 // distinguishable from bitmaps in the resource pak. | 95 image, |
96 skia::ImageOperations::RESIZE_LANCZOS3, | |
97 RoundToInt(image.width() * scale), | |
98 RoundToInt(image.height() * scale)); | |
oshima
2012/10/10 21:43:29
Exactly how conversion should work depends on how
benrg
2012/10/11 01:22:02
That's fine with me. Everything is going to have t
| |
99 // If --highlight-missing-scaled-resources is specified, log the resource | |
100 // id and blend the created resource with red. | |
89 if (ShouldHighlightMissingScaledResources()) { | 101 if (ShouldHighlightMissingScaledResources()) { |
90 if (!result.get()) { | 102 LOG(ERROR) << "Missing " << scale << "x scaled resource. id=" |
91 LOG(ERROR) << "Missing " << scale << "x resource. id=" | 103 << resource_id_; |
92 << resource_id_; | |
93 } else { | |
94 LOG(ERROR) << "Incorrectly sized " << scale << "x resource. id=" | |
95 << resource_id_; | |
96 } | |
97 | |
98 scoped_ptr<SkBitmap> bitmap1x( | |
99 rb_->LoadBitmap(resource_id_, SCALE_FACTOR_100P)); | |
100 DCHECK(bitmap1x.get()); | |
101 SkBitmap bitmap_scaled = skia::ImageOperations::Resize( | |
102 *bitmap1x, | |
103 skia::ImageOperations::RESIZE_LANCZOS3, | |
104 size_in_pixel.width(), | |
105 size_in_pixel.height()); | |
106 | 104 |
107 SkBitmap mask; | 105 SkBitmap mask; |
108 mask.setConfig(SkBitmap::kARGB_8888_Config, | 106 mask.setConfig(SkBitmap::kARGB_8888_Config, |
109 bitmap_scaled.width(), | 107 image.width(), image.height()); |
110 bitmap_scaled.height()); | |
111 mask.allocPixels(); | 108 mask.allocPixels(); |
112 mask.eraseColor(SK_ColorRED); | 109 mask.eraseColor(SK_ColorRED); |
113 result.reset(new SkBitmap()); | 110 image = SkBitmapOperations::CreateBlendedBitmap(image, mask, 0.2); |
114 *result.get() = SkBitmapOperations::CreateBlendedBitmap( | |
115 bitmap_scaled, mask, 0.2); | |
116 } else if (!result.get() || result->width() == size_in_dip_.width()) { | |
117 // The scaled resource pack may have the 1x image if its grd file | |
118 // points to 1x image. Fallback to 1x by returning empty image | |
119 // in this case. This 1x image will be scaled when drawn. | |
120 return gfx::ImageSkiaRep(); | |
121 } | 111 } |
122 // If the size of scaled image isn't exactly |scale| * 1x version, | |
123 // create ImageSkia as usual. This will end up with | |
124 // corrupted visual representation as the size of image doesn't | |
125 // match the expected size. | |
126 } | 112 } |
127 DCHECK(result.get()); | 113 |
128 return gfx::ImageSkiaRep(*result.get(), scale_factor); | 114 return gfx::ImageSkiaRep(image, scale_factor); |
129 } | 115 } |
130 | 116 |
131 private: | 117 private: |
132 ResourceBundle* rb_; | 118 ResourceBundle* rb_; |
133 const int resource_id_; | 119 const int resource_id_; |
134 const gfx::Size size_in_dip_; | |
135 | 120 |
136 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); | 121 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); |
137 }; | 122 }; |
138 | 123 |
139 // static | 124 // static |
140 std::string ResourceBundle::InitSharedInstanceWithLocale( | 125 std::string ResourceBundle::InitSharedInstanceWithLocale( |
141 const std::string& pref_locale, Delegate* delegate) { | 126 const std::string& pref_locale, Delegate* delegate) { |
142 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; | 127 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; |
143 g_shared_instance_ = new ResourceBundle(delegate); | 128 g_shared_instance_ = new ResourceBundle(delegate); |
144 | 129 |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 } | 318 } |
334 | 319 |
335 gfx::Image image; | 320 gfx::Image image; |
336 if (delegate_) | 321 if (delegate_) |
337 image = delegate_->GetImageNamed(resource_id); | 322 image = delegate_->GetImageNamed(resource_id); |
338 | 323 |
339 if (image.IsEmpty()) { | 324 if (image.IsEmpty()) { |
340 DCHECK(!delegate_ && !data_packs_.empty()) << | 325 DCHECK(!delegate_ && !data_packs_.empty()) << |
341 "Missing call to SetResourcesDataDLL?"; | 326 "Missing call to SetResourcesDataDLL?"; |
342 | 327 |
343 // TODO(oshima): Pick the scale factor from currently used scale factors. | 328 // TODO(oshima): This should be GetPrimaryDisplay().device_scale_factor(), |
344 scoped_ptr<SkBitmap> bitmap(LoadBitmap(resource_id, SCALE_FACTOR_100P)); | 329 // but GetPrimaryDisplay() crashes at startup. |
345 if (!bitmap.get()) { | 330 ScaleFactor primary_scale_factor = SCALE_FACTOR_100P; |
331 // ResourceBundle::GetSharedInstance() is destroyed after the | |
332 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be | |
333 // destroyed before the resource bundle is destroyed. | |
334 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id), | |
335 primary_scale_factor); | |
336 if (image_skia.isNull()) { | |
346 LOG(WARNING) << "Unable to load image with id " << resource_id; | 337 LOG(WARNING) << "Unable to load image with id " << resource_id; |
347 NOTREACHED(); // Want to assert in debug mode. | 338 NOTREACHED(); // Want to assert in debug mode. |
348 // The load failed to retrieve the image; show a debugging red square. | 339 // The load failed to retrieve the image; show a debugging red square. |
349 return GetEmptyImage(); | 340 return GetEmptyImage(); |
350 } | 341 } |
351 | |
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::Size size_in_dip(bitmap->width(), bitmap->height()); | |
356 gfx::ImageSkia image_skia( | |
357 new ResourceBundleImageSource(this, resource_id, size_in_dip), | |
358 size_in_dip); | |
359 image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap.get(), | |
360 SCALE_FACTOR_100P)); | |
361 image_skia.SetReadOnly(); | 342 image_skia.SetReadOnly(); |
362 image = gfx::Image(image_skia); | 343 image = gfx::Image(image_skia); |
363 } | 344 } |
364 | 345 |
365 // The load was successful, so cache the image. | 346 // The load was successful, so cache the image. |
366 base::AutoLock lock_scope(*images_and_fonts_lock_); | 347 base::AutoLock lock_scope(*images_and_fonts_lock_); |
367 | 348 |
368 // Another thread raced the load and has already cached the image. | 349 // Another thread raced the load and has already cached the image. |
369 if (images_.count(resource_id)) | 350 if (images_.count(resource_id)) |
370 return images_[resource_id]; | 351 return images_[resource_id]; |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
578 | 559 |
579 if (!large_bold_font_.get()) { | 560 if (!large_bold_font_.get()) { |
580 large_bold_font_.reset(new gfx::Font()); | 561 large_bold_font_.reset(new gfx::Font()); |
581 *large_bold_font_ = | 562 *large_bold_font_ = |
582 base_font_->DeriveFont(kLargeFontSizeDelta, | 563 base_font_->DeriveFont(kLargeFontSizeDelta, |
583 base_font_->GetStyle() | gfx::Font::BOLD); | 564 base_font_->GetStyle() | gfx::Font::BOLD); |
584 } | 565 } |
585 } | 566 } |
586 } | 567 } |
587 | 568 |
588 SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, | 569 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, |
589 int resource_id) const { | 570 int resource_id, |
571 SkBitmap* bitmap, | |
572 bool* fell_back_to_1x) const { | |
590 scoped_refptr<base::RefCountedMemory> memory( | 573 scoped_refptr<base::RefCountedMemory> memory( |
591 data_handle.GetStaticMemory(resource_id)); | 574 data_handle.GetStaticMemory(resource_id)); |
592 if (!memory) | 575 if (!memory) |
593 return NULL; | 576 return false; |
594 | 577 |
595 SkBitmap bitmap; | 578 if (gfx::PNGCodec::Decode(memory->front(), memory->size(), |
596 if (gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) | 579 bitmap, fell_back_to_1x)) { |
597 return new SkBitmap(bitmap); | 580 return true; |
581 } | |
598 | 582 |
599 #if !defined(OS_IOS) | 583 #if !defined(OS_IOS) |
600 // iOS does not compile or use the JPEG codec. On other platforms, | 584 // iOS does not compile or use the JPEG codec. On other platforms, |
601 // 99% of our assets are PNGs, however fallback to JPEG. | 585 // 99% of our assets are PNGs, however fallback to JPEG. |
602 SkBitmap* allocated_bitmap = | 586 scoped_ptr<SkBitmap> jpeg_bitmap( |
603 gfx::JPEGCodec::Decode(memory->front(), memory->size()); | 587 gfx::JPEGCodec::Decode(memory->front(), memory->size())); |
604 if (allocated_bitmap) | 588 if (jpeg_bitmap.get()) { |
605 return allocated_bitmap; | 589 bitmap->swap(*jpeg_bitmap.get()); |
590 return true; | |
591 } | |
606 #endif | 592 #endif |
607 | 593 |
608 NOTREACHED() << "Unable to decode theme image resource " << resource_id; | 594 NOTREACHED() << "Unable to decode theme image resource " << resource_id; |
609 return NULL; | 595 return false; |
610 } | 596 } |
611 | 597 |
612 SkBitmap* ResourceBundle::LoadBitmap(int resource_id, | 598 bool ResourceBundle::LoadBitmap(int resource_id, |
613 ScaleFactor scale_factor) const { | 599 ScaleFactor scale_factor, |
600 SkBitmap* bitmap, | |
601 bool* fell_back_to_1x) const { | |
614 for (size_t i = 0; i < data_packs_.size(); ++i) { | 602 for (size_t i = 0; i < data_packs_.size(); ++i) { |
615 if (data_packs_[i]->GetScaleFactor() == scale_factor) { | 603 if (data_packs_[i]->GetScaleFactor() == scale_factor) { |
616 SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); | 604 if (LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) |
617 if (bitmap) | 605 return true; |
618 return bitmap; | |
619 } | 606 } |
620 } | 607 } |
621 return NULL; | 608 return false; |
622 } | 609 } |
623 | 610 |
624 gfx::Image& ResourceBundle::GetEmptyImage() { | 611 gfx::Image& ResourceBundle::GetEmptyImage() { |
625 base::AutoLock lock(*images_and_fonts_lock_); | 612 base::AutoLock lock(*images_and_fonts_lock_); |
626 | 613 |
627 if (empty_image_.IsEmpty()) { | 614 if (empty_image_.IsEmpty()) { |
628 // The placeholder bitmap is bright red so people notice the problem. | 615 // The placeholder bitmap is bright red so people notice the problem. |
629 SkBitmap bitmap; | 616 SkBitmap bitmap; |
630 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); | 617 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); |
631 bitmap.allocPixels(); | 618 bitmap.allocPixels(); |
632 bitmap.eraseARGB(255, 255, 0, 0); | 619 bitmap.eraseARGB(255, 255, 0, 0); |
633 empty_image_ = gfx::Image(bitmap); | 620 empty_image_ = gfx::Image(bitmap); |
634 } | 621 } |
635 return empty_image_; | 622 return empty_image_; |
636 } | 623 } |
637 | 624 |
638 } // namespace ui | 625 } // namespace ui |
OLD | NEW |