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

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: back out libpng/PNGCodec changes, scan for special chunks by hand (and rebase) 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
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 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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698