Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(464)

Side by Side Diff: ui/base/resource/resource_bundle.cc

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

Powered by Google App Engine
This is Rietveld 408576698