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 "net/base/big_endian.h" | |
20 #include "skia/ext/image_operations.h" | 21 #include "skia/ext/image_operations.h" |
21 #include "third_party/skia/include/core/SkBitmap.h" | 22 #include "third_party/skia/include/core/SkBitmap.h" |
22 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" |
23 #include "ui/base/layout.h" | 24 #include "ui/base/layout.h" |
24 #include "ui/base/resource/data_pack.h" | 25 #include "ui/base/resource/data_pack.h" |
25 #include "ui/base/ui_base_paths.h" | 26 #include "ui/base/ui_base_paths.h" |
26 #include "ui/base/ui_base_switches.h" | 27 #include "ui/base/ui_base_switches.h" |
27 #include "ui/gfx/codec/jpeg_codec.h" | 28 #include "ui/gfx/codec/jpeg_codec.h" |
28 #include "ui/gfx/codec/png_codec.h" | 29 #include "ui/gfx/codec/png_codec.h" |
29 #include "ui/gfx/image/image_skia.h" | 30 #include "ui/gfx/image/image_skia.h" |
30 #include "ui/gfx/image/image_skia_source.h" | 31 #include "ui/gfx/image/image_skia_source.h" |
32 #include "ui/gfx/safe_integer_conversions.h" | |
31 #include "ui/gfx/screen.h" | 33 #include "ui/gfx/screen.h" |
32 #include "ui/gfx/size_conversions.h" | 34 #include "ui/gfx/size_conversions.h" |
33 #include "ui/gfx/skbitmap_operations.h" | 35 #include "ui/gfx/skbitmap_operations.h" |
34 | 36 |
35 namespace ui { | 37 namespace ui { |
36 | 38 |
37 namespace { | 39 namespace { |
38 | 40 |
39 // Font sizes relative to base font. | 41 // Font sizes relative to base font. |
40 const int kSmallFontSizeDelta = -2; | 42 const int kSmallFontSizeDelta = -2; |
41 const int kMediumFontSizeDelta = 3; | 43 const int kMediumFontSizeDelta = 3; |
42 const int kLargeFontSizeDelta = 8; | 44 const int kLargeFontSizeDelta = 8; |
43 | 45 |
46 const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; | |
47 | |
44 ResourceBundle* g_shared_instance_ = NULL; | 48 ResourceBundle* g_shared_instance_ = NULL; |
45 | 49 |
46 bool ShouldHighlightMissingScaledResources() { | 50 bool ShouldHighlightMissingScaledResources() { |
47 return CommandLine::ForCurrentProcess()->HasSwitch( | 51 return CommandLine::ForCurrentProcess()->HasSwitch( |
48 switches::kHighlightMissingScaledResources); | 52 switches::kHighlightMissingScaledResources); |
49 } | 53 } |
50 | 54 |
55 // A wrapper for PNGCodec::Decode that returns information about custom chunks. | |
56 // For security reasons we can't alter PNGCodec to return this information. Our | |
57 // PNG files are preprocessed by GRIT to move our custom chunks to the beginning | |
58 // (before even the PNG header), and we omit them entirely from the data that we | |
59 // pass on to PNGCodec::Decode. | |
60 bool DecodePNG(const unsigned char* buf, | |
61 size_t size, | |
62 SkBitmap* bitmap, | |
63 bool* fell_back_to_1x) { | |
64 bool found_cscl = false; | |
65 size_t pos = 0; | |
66 // Scan for special chunks until we find the PNG header. | |
67 for (;;) { | |
68 if (size - pos < 12) | |
oshima
2012/10/16 23:12:09
can you define constants for these literals? (12,
benrg
2012/10/17 15:57:25
Done.
| |
69 return false; | |
70 if (memcmp(buf + pos, kPngMagic, 8) == 0) | |
71 break; | |
72 uint32 length; | |
73 net::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length); | |
74 if (size - pos - 12 < length) | |
75 return false; | |
76 if (length == 0 && memcmp(buf + pos + 4, "csCl", 4) == 0) | |
77 found_cscl = true; | |
oshima
2012/10/16 23:12:09
why not just *fell_back_to_1x = true?
benrg
2012/10/17 15:57:25
Done.
| |
78 pos += length + 12; | |
79 } | |
80 *fell_back_to_1x = found_cscl; | |
81 // Pass the rest of the data to the PNG decoder. | |
82 return gfx::PNGCodec::Decode(buf + pos, size - pos, bitmap); | |
83 } | |
84 | |
51 } // namespace | 85 } // namespace |
52 | 86 |
53 // An ImageSkiaSource that loads bitmaps for requested scale factor from | 87 // An ImageSkiaSource that loads bitmaps for the requested scale factor from |
54 // ResourceBundle on demand for given resource_id. It falls back | 88 // 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 | 89 // 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 | 90 // 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 | 91 // 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. | 92 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images |
59 // When --highlight-missing-scaled-resources flag is specified, it | 93 // are higlighted by blending them with red. |
60 // will show the scaled image blended with red instead. | |
61 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { | 94 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { |
62 public: | 95 public: |
63 ResourceBundleImageSource(ResourceBundle* rb, | 96 ResourceBundleImageSource(ResourceBundle* rb, int resource_id) |
64 int resource_id, | 97 : 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() {} | 98 virtual ~ResourceBundleImageSource() {} |
71 | 99 |
72 // gfx::ImageSkiaSource overrides: | 100 // gfx::ImageSkiaSource overrides: |
73 virtual gfx::ImageSkiaRep GetImageForScale( | 101 virtual gfx::ImageSkiaRep GetImageForScale( |
74 ui::ScaleFactor scale_factor) OVERRIDE { | 102 ui::ScaleFactor scale_factor) OVERRIDE { |
75 scoped_ptr<SkBitmap> result(rb_->LoadBitmap(resource_id_, scale_factor)); | 103 SkBitmap image; |
76 float scale = ui::GetScaleFactorScale(scale_factor); | 104 bool fell_back_to_1x = false; |
77 gfx::Size size_in_pixel = gfx::ToFlooredSize(size_in_dip_.Scale(scale)); | 105 bool found = rb_->LoadBitmap(resource_id_, scale_factor, |
106 &image, &fell_back_to_1x); | |
107 if (!found) | |
108 return gfx::ImageSkiaRep(); | |
78 | 109 |
79 if (scale_factor != SCALE_FACTOR_100P && | 110 if (fell_back_to_1x) { |
80 (!result.get() || | 111 // GRIT fell back to the 100% image, so rescale it to the correct size. |
81 result->width() != size_in_pixel.width() || | 112 float scale = GetScaleFactorScale(scale_factor); |
82 result->height() != size_in_pixel.height())) { | 113 image = skia::ImageOperations::Resize( |
83 | 114 image, |
84 // If non 1x resource is missing from |image| or is the incorrect | 115 skia::ImageOperations::RESIZE_LANCZOS3, |
85 // size and --highlight-missing-scaled-resources is specified, logs | 116 gfx::ToFlooredInt(image.width() * scale), |
86 // the resource id and creates a version of the resource at the correct | 117 gfx::ToFlooredInt(image.height() * scale)); |
87 // size. Blends the created resource with red to make it | 118 // If --highlight-missing-scaled-resources is specified, log the resource |
88 // distinguishable from bitmaps in the resource pak. | 119 // id and blend the created resource with red. |
89 if (ShouldHighlightMissingScaledResources()) { | 120 if (ShouldHighlightMissingScaledResources()) { |
90 if (!result.get()) { | 121 LOG(ERROR) << "Missing " << scale << "x scaled resource. id=" |
91 LOG(ERROR) << "Missing " << scale << "x resource. id=" | 122 << 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 | 123 |
107 SkBitmap mask; | 124 SkBitmap mask; |
108 mask.setConfig(SkBitmap::kARGB_8888_Config, | 125 mask.setConfig(SkBitmap::kARGB_8888_Config, |
109 bitmap_scaled.width(), | 126 image.width(), image.height()); |
110 bitmap_scaled.height()); | |
111 mask.allocPixels(); | 127 mask.allocPixels(); |
112 mask.eraseColor(SK_ColorRED); | 128 mask.eraseColor(SK_ColorRED); |
113 result.reset(new SkBitmap()); | 129 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 } | 130 } |
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 } | 131 } |
127 DCHECK(result.get()); | 132 |
128 return gfx::ImageSkiaRep(*result.get(), scale_factor); | 133 return gfx::ImageSkiaRep(image, scale_factor); |
129 } | 134 } |
130 | 135 |
131 private: | 136 private: |
132 ResourceBundle* rb_; | 137 ResourceBundle* rb_; |
133 const int resource_id_; | 138 const int resource_id_; |
134 const gfx::Size size_in_dip_; | |
135 | 139 |
136 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); | 140 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); |
137 }; | 141 }; |
138 | 142 |
139 // static | 143 // static |
140 std::string ResourceBundle::InitSharedInstanceWithLocale( | 144 std::string ResourceBundle::InitSharedInstanceWithLocale( |
141 const std::string& pref_locale, Delegate* delegate) { | 145 const std::string& pref_locale, Delegate* delegate) { |
142 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; | 146 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; |
143 g_shared_instance_ = new ResourceBundle(delegate); | 147 g_shared_instance_ = new ResourceBundle(delegate); |
144 | 148 |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
343 } | 347 } |
344 | 348 |
345 gfx::Image image; | 349 gfx::Image image; |
346 if (delegate_) | 350 if (delegate_) |
347 image = delegate_->GetImageNamed(resource_id); | 351 image = delegate_->GetImageNamed(resource_id); |
348 | 352 |
349 if (image.IsEmpty()) { | 353 if (image.IsEmpty()) { |
350 DCHECK(!delegate_ && !data_packs_.empty()) << | 354 DCHECK(!delegate_ && !data_packs_.empty()) << |
351 "Missing call to SetResourcesDataDLL?"; | 355 "Missing call to SetResourcesDataDLL?"; |
352 | 356 |
353 // TODO(oshima): Pick the scale factor from currently used scale factors. | 357 // TODO(oshima): This should be GetPrimaryDisplay().device_scale_factor(), |
354 scoped_ptr<SkBitmap> bitmap(LoadBitmap(resource_id, SCALE_FACTOR_100P)); | 358 // but GetPrimaryDisplay() crashes at startup. |
355 if (!bitmap.get()) { | 359 ScaleFactor primary_scale_factor = SCALE_FACTOR_100P; |
360 // ResourceBundle::GetSharedInstance() is destroyed after the | |
361 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be | |
362 // destroyed before the resource bundle is destroyed. | |
363 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id), | |
364 primary_scale_factor); | |
365 if (image_skia.isNull()) { | |
356 LOG(WARNING) << "Unable to load image with id " << resource_id; | 366 LOG(WARNING) << "Unable to load image with id " << resource_id; |
357 NOTREACHED(); // Want to assert in debug mode. | 367 NOTREACHED(); // Want to assert in debug mode. |
358 // The load failed to retrieve the image; show a debugging red square. | 368 // The load failed to retrieve the image; show a debugging red square. |
359 return GetEmptyImage(); | 369 return GetEmptyImage(); |
360 } | 370 } |
361 | |
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::Size size_in_dip(bitmap->width(), bitmap->height()); | |
366 gfx::ImageSkia image_skia( | |
367 new ResourceBundleImageSource(this, resource_id, size_in_dip), | |
368 size_in_dip); | |
369 image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap.get(), | |
370 SCALE_FACTOR_100P)); | |
371 image_skia.SetReadOnly(); | 371 image_skia.SetReadOnly(); |
372 image = gfx::Image(image_skia); | 372 image = gfx::Image(image_skia); |
373 } | 373 } |
374 | 374 |
375 // The load was successful, so cache the image. | 375 // The load was successful, so cache the image. |
376 base::AutoLock lock_scope(*images_and_fonts_lock_); | 376 base::AutoLock lock_scope(*images_and_fonts_lock_); |
377 | 377 |
378 // Another thread raced the load and has already cached the image. | 378 // Another thread raced the load and has already cached the image. |
379 if (images_.count(resource_id)) | 379 if (images_.count(resource_id)) |
380 return images_[resource_id]; | 380 return images_[resource_id]; |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
588 | 588 |
589 if (!large_bold_font_.get()) { | 589 if (!large_bold_font_.get()) { |
590 large_bold_font_.reset(new gfx::Font()); | 590 large_bold_font_.reset(new gfx::Font()); |
591 *large_bold_font_ = | 591 *large_bold_font_ = |
592 base_font_->DeriveFont(kLargeFontSizeDelta, | 592 base_font_->DeriveFont(kLargeFontSizeDelta, |
593 base_font_->GetStyle() | gfx::Font::BOLD); | 593 base_font_->GetStyle() | gfx::Font::BOLD); |
594 } | 594 } |
595 } | 595 } |
596 } | 596 } |
597 | 597 |
598 SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, | 598 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, |
599 int resource_id) const { | 599 int resource_id, |
600 SkBitmap* bitmap, | |
601 bool* fell_back_to_1x) const { | |
600 scoped_refptr<base::RefCountedMemory> memory( | 602 scoped_refptr<base::RefCountedMemory> memory( |
601 data_handle.GetStaticMemory(resource_id)); | 603 data_handle.GetStaticMemory(resource_id)); |
602 if (!memory) | 604 if (!memory) |
603 return NULL; | 605 return false; |
604 | 606 |
605 SkBitmap bitmap; | 607 if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x)) |
606 if (gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) | 608 return true; |
607 return new SkBitmap(bitmap); | |
608 | 609 |
609 #if !defined(OS_IOS) | 610 #if !defined(OS_IOS) |
610 // iOS does not compile or use the JPEG codec. On other platforms, | 611 // iOS does not compile or use the JPEG codec. On other platforms, |
611 // 99% of our assets are PNGs, however fallback to JPEG. | 612 // 99% of our assets are PNGs, however fallback to JPEG. |
612 SkBitmap* allocated_bitmap = | 613 scoped_ptr<SkBitmap> jpeg_bitmap( |
613 gfx::JPEGCodec::Decode(memory->front(), memory->size()); | 614 gfx::JPEGCodec::Decode(memory->front(), memory->size())); |
614 if (allocated_bitmap) | 615 if (jpeg_bitmap.get()) { |
615 return allocated_bitmap; | 616 bitmap->swap(*jpeg_bitmap.get()); |
617 *fell_back_to_1x = false; | |
618 return true; | |
619 } | |
616 #endif | 620 #endif |
617 | 621 |
618 NOTREACHED() << "Unable to decode theme image resource " << resource_id; | 622 NOTREACHED() << "Unable to decode theme image resource " << resource_id; |
619 return NULL; | 623 return false; |
620 } | 624 } |
621 | 625 |
622 SkBitmap* ResourceBundle::LoadBitmap(int resource_id, | 626 bool ResourceBundle::LoadBitmap(int resource_id, |
623 ScaleFactor scale_factor) const { | 627 ScaleFactor scale_factor, |
628 SkBitmap* bitmap, | |
629 bool* fell_back_to_1x) const { | |
624 for (size_t i = 0; i < data_packs_.size(); ++i) { | 630 for (size_t i = 0; i < data_packs_.size(); ++i) { |
625 if (data_packs_[i]->GetScaleFactor() == scale_factor) { | 631 if (data_packs_[i]->GetScaleFactor() == scale_factor) { |
626 SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); | 632 if (LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) |
627 if (bitmap) | 633 return true; |
628 return bitmap; | |
629 } | 634 } |
630 } | 635 } |
631 return NULL; | 636 return false; |
632 } | 637 } |
633 | 638 |
634 gfx::Image& ResourceBundle::GetEmptyImage() { | 639 gfx::Image& ResourceBundle::GetEmptyImage() { |
635 base::AutoLock lock(*images_and_fonts_lock_); | 640 base::AutoLock lock(*images_and_fonts_lock_); |
636 | 641 |
637 if (empty_image_.IsEmpty()) { | 642 if (empty_image_.IsEmpty()) { |
638 // The placeholder bitmap is bright red so people notice the problem. | 643 // The placeholder bitmap is bright red so people notice the problem. |
639 SkBitmap bitmap; | 644 SkBitmap bitmap; |
640 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); | 645 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); |
641 bitmap.allocPixels(); | 646 bitmap.allocPixels(); |
642 bitmap.eraseARGB(255, 255, 0, 0); | 647 bitmap.eraseARGB(255, 255, 0, 0); |
643 empty_image_ = gfx::Image(bitmap); | 648 empty_image_ = gfx::Image(bitmap); |
644 } | 649 } |
645 return empty_image_; | 650 return empty_image_; |
646 } | 651 } |
647 | 652 |
648 } // namespace ui | 653 } // namespace ui |
OLD | NEW |