Index: ui/base/resource/resource_bundle.cc |
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc |
index bb1e532f5cefeda1f976792ab45394e5f19d1b69..7e2f4548c6d86a516671022505af829740af828c 100644 |
--- a/ui/base/resource/resource_bundle.cc |
+++ b/ui/base/resource/resource_bundle.cc |
@@ -17,6 +17,7 @@ |
#include "base/synchronization/lock.h" |
#include "base/utf_string_conversions.h" |
#include "build/build_config.h" |
+#include "net/base/big_endian.h" |
#include "skia/ext/image_operations.h" |
#include "third_party/skia/include/core/SkBitmap.h" |
#include "ui/base/l10n/l10n_util.h" |
@@ -28,6 +29,7 @@ |
#include "ui/gfx/codec/png_codec.h" |
#include "ui/gfx/image/image_skia.h" |
#include "ui/gfx/image/image_skia_source.h" |
+#include "ui/gfx/safe_integer_conversions.h" |
#include "ui/gfx/screen.h" |
#include "ui/gfx/size_conversions.h" |
#include "ui/gfx/skbitmap_operations.h" |
@@ -41,6 +43,8 @@ const int kSmallFontSizeDelta = -2; |
const int kMediumFontSizeDelta = 3; |
const int kLargeFontSizeDelta = 8; |
+const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; |
+ |
ResourceBundle* g_shared_instance_ = NULL; |
bool ShouldHighlightMissingScaledResources() { |
@@ -48,90 +52,90 @@ bool ShouldHighlightMissingScaledResources() { |
switches::kHighlightMissingScaledResources); |
} |
+// A wrapper for PNGCodec::Decode that returns information about custom chunks. |
+// For security reasons we can't alter PNGCodec to return this information. Our |
+// PNG files are preprocessed by GRIT to move our custom chunks to the beginning |
+// (before even the PNG header), and we omit them entirely from the data that we |
+// pass on to PNGCodec::Decode. |
+bool DecodePNG(const unsigned char* buf, |
+ size_t size, |
+ SkBitmap* bitmap, |
+ bool* fell_back_to_1x) { |
+ bool found_cscl = false; |
+ size_t pos = 0; |
+ // Scan for special chunks until we find the PNG header. |
+ for (;;) { |
+ 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.
|
+ return false; |
+ if (memcmp(buf + pos, kPngMagic, 8) == 0) |
+ break; |
+ uint32 length; |
+ net::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length); |
+ if (size - pos - 12 < length) |
+ return false; |
+ if (length == 0 && memcmp(buf + pos + 4, "csCl", 4) == 0) |
+ 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.
|
+ pos += length + 12; |
+ } |
+ *fell_back_to_1x = found_cscl; |
+ // Pass the rest of the data to the PNG decoder. |
+ return gfx::PNGCodec::Decode(buf + pos, size - pos, bitmap); |
+} |
+ |
} // namespace |
-// An ImageSkiaSource that loads bitmaps for requested scale factor from |
-// ResourceBundle on demand for given resource_id. It falls back |
-// to the 1x bitmap if the bitmap for the requested scale factor does not |
-// exist. If the resource for the requested scale factor is not exactly |
-// |scale_factor| * the size of the 1x resource, it will end up with |
-// broken UI because it will be drawn as if the bitmap was the correct size. |
-// When --highlight-missing-scaled-resources flag is specified, it |
-// will show the scaled image blended with red instead. |
+// An ImageSkiaSource that loads bitmaps for the requested scale factor from |
+// ResourceBundle on demand for a given |resource_id|. If the bitmap for the |
+// requested scale factor does not exist, it will return the 1x bitmap scaled |
+// by the scale factor. This may lead to broken UI if the correct size of the |
+// scaled image is not exactly |scale_factor| * the size of the 1x resource. |
+// When --highlight-missing-scaled-resources flag is specified, scaled 1x images |
+// are higlighted by blending them with red. |
class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { |
public: |
- ResourceBundleImageSource(ResourceBundle* rb, |
- int resource_id, |
- const gfx::Size& size_in_dip) |
- : rb_(rb), |
- resource_id_(resource_id), |
- size_in_dip_(size_in_dip) { |
- } |
+ ResourceBundleImageSource(ResourceBundle* rb, int resource_id) |
+ : rb_(rb), resource_id_(resource_id) {} |
virtual ~ResourceBundleImageSource() {} |
// gfx::ImageSkiaSource overrides: |
virtual gfx::ImageSkiaRep GetImageForScale( |
ui::ScaleFactor scale_factor) OVERRIDE { |
- scoped_ptr<SkBitmap> result(rb_->LoadBitmap(resource_id_, scale_factor)); |
- float scale = ui::GetScaleFactorScale(scale_factor); |
- gfx::Size size_in_pixel = gfx::ToFlooredSize(size_in_dip_.Scale(scale)); |
- |
- if (scale_factor != SCALE_FACTOR_100P && |
- (!result.get() || |
- result->width() != size_in_pixel.width() || |
- result->height() != size_in_pixel.height())) { |
- |
- // If non 1x resource is missing from |image| or is the incorrect |
- // size and --highlight-missing-scaled-resources is specified, logs |
- // the resource id and creates a version of the resource at the correct |
- // size. Blends the created resource with red to make it |
- // distinguishable from bitmaps in the resource pak. |
+ SkBitmap image; |
+ bool fell_back_to_1x = false; |
+ bool found = rb_->LoadBitmap(resource_id_, scale_factor, |
+ &image, &fell_back_to_1x); |
+ if (!found) |
+ return gfx::ImageSkiaRep(); |
+ |
+ if (fell_back_to_1x) { |
+ // GRIT fell back to the 100% image, so rescale it to the correct size. |
+ float scale = GetScaleFactorScale(scale_factor); |
+ image = skia::ImageOperations::Resize( |
+ image, |
+ skia::ImageOperations::RESIZE_LANCZOS3, |
+ gfx::ToFlooredInt(image.width() * scale), |
+ gfx::ToFlooredInt(image.height() * scale)); |
+ // If --highlight-missing-scaled-resources is specified, log the resource |
+ // id and blend the created resource with red. |
if (ShouldHighlightMissingScaledResources()) { |
- if (!result.get()) { |
- LOG(ERROR) << "Missing " << scale << "x resource. id=" |
- << resource_id_; |
- } else { |
- LOG(ERROR) << "Incorrectly sized " << scale << "x resource. id=" |
- << resource_id_; |
- } |
- |
- scoped_ptr<SkBitmap> bitmap1x( |
- rb_->LoadBitmap(resource_id_, SCALE_FACTOR_100P)); |
- DCHECK(bitmap1x.get()); |
- SkBitmap bitmap_scaled = skia::ImageOperations::Resize( |
- *bitmap1x, |
- skia::ImageOperations::RESIZE_LANCZOS3, |
- size_in_pixel.width(), |
- size_in_pixel.height()); |
+ LOG(ERROR) << "Missing " << scale << "x scaled resource. id=" |
+ << resource_id_; |
SkBitmap mask; |
mask.setConfig(SkBitmap::kARGB_8888_Config, |
- bitmap_scaled.width(), |
- bitmap_scaled.height()); |
+ image.width(), image.height()); |
mask.allocPixels(); |
mask.eraseColor(SK_ColorRED); |
- result.reset(new SkBitmap()); |
- *result.get() = SkBitmapOperations::CreateBlendedBitmap( |
- bitmap_scaled, mask, 0.2); |
- } else if (!result.get() || result->width() == size_in_dip_.width()) { |
- // The scaled resource pack may have the 1x image if its grd file |
- // points to 1x image. Fallback to 1x by returning empty image |
- // in this case. This 1x image will be scaled when drawn. |
- return gfx::ImageSkiaRep(); |
+ image = SkBitmapOperations::CreateBlendedBitmap(image, mask, 0.2); |
} |
- // If the size of scaled image isn't exactly |scale| * 1x version, |
- // create ImageSkia as usual. This will end up with |
- // corrupted visual representation as the size of image doesn't |
- // match the expected size. |
} |
- DCHECK(result.get()); |
- return gfx::ImageSkiaRep(*result.get(), scale_factor); |
+ |
+ return gfx::ImageSkiaRep(image, scale_factor); |
} |
private: |
ResourceBundle* rb_; |
const int resource_id_; |
- const gfx::Size size_in_dip_; |
DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource); |
}; |
@@ -350,24 +354,20 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { |
DCHECK(!delegate_ && !data_packs_.empty()) << |
"Missing call to SetResourcesDataDLL?"; |
- // TODO(oshima): Pick the scale factor from currently used scale factors. |
- scoped_ptr<SkBitmap> bitmap(LoadBitmap(resource_id, SCALE_FACTOR_100P)); |
- if (!bitmap.get()) { |
+ // TODO(oshima): This should be GetPrimaryDisplay().device_scale_factor(), |
+ // but GetPrimaryDisplay() crashes at startup. |
+ ScaleFactor primary_scale_factor = SCALE_FACTOR_100P; |
+ // ResourceBundle::GetSharedInstance() is destroyed after the |
+ // BrowserMainLoop has finished running. |image_skia| is guaranteed to be |
+ // destroyed before the resource bundle is destroyed. |
+ gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id), |
+ primary_scale_factor); |
+ if (image_skia.isNull()) { |
LOG(WARNING) << "Unable to load image with id " << resource_id; |
NOTREACHED(); // Want to assert in debug mode. |
// The load failed to retrieve the image; show a debugging red square. |
return GetEmptyImage(); |
} |
- |
- // ResourceBundle::GetSharedInstance() is destroyed after the |
- // BrowserMainLoop has finished running. |image_skia| is guaranteed to be |
- // destroyed before the resource bundle is destroyed. |
- gfx::Size size_in_dip(bitmap->width(), bitmap->height()); |
- gfx::ImageSkia image_skia( |
- new ResourceBundleImageSource(this, resource_id, size_in_dip), |
- size_in_dip); |
- image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap.get(), |
- SCALE_FACTOR_100P)); |
image_skia.SetReadOnly(); |
image = gfx::Image(image_skia); |
} |
@@ -595,40 +595,45 @@ void ResourceBundle::LoadFontsIfNecessary() { |
} |
} |
-SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, |
- int resource_id) const { |
+bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, |
+ int resource_id, |
+ SkBitmap* bitmap, |
+ bool* fell_back_to_1x) const { |
scoped_refptr<base::RefCountedMemory> memory( |
data_handle.GetStaticMemory(resource_id)); |
if (!memory) |
- return NULL; |
+ return false; |
- SkBitmap bitmap; |
- if (gfx::PNGCodec::Decode(memory->front(), memory->size(), &bitmap)) |
- return new SkBitmap(bitmap); |
+ if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x)) |
+ return true; |
#if !defined(OS_IOS) |
// iOS does not compile or use the JPEG codec. On other platforms, |
// 99% of our assets are PNGs, however fallback to JPEG. |
- SkBitmap* allocated_bitmap = |
- gfx::JPEGCodec::Decode(memory->front(), memory->size()); |
- if (allocated_bitmap) |
- return allocated_bitmap; |
+ scoped_ptr<SkBitmap> jpeg_bitmap( |
+ gfx::JPEGCodec::Decode(memory->front(), memory->size())); |
+ if (jpeg_bitmap.get()) { |
+ bitmap->swap(*jpeg_bitmap.get()); |
+ *fell_back_to_1x = false; |
+ return true; |
+ } |
#endif |
NOTREACHED() << "Unable to decode theme image resource " << resource_id; |
- return NULL; |
+ return false; |
} |
-SkBitmap* ResourceBundle::LoadBitmap(int resource_id, |
- ScaleFactor scale_factor) const { |
+bool ResourceBundle::LoadBitmap(int resource_id, |
+ ScaleFactor scale_factor, |
+ SkBitmap* bitmap, |
+ bool* fell_back_to_1x) const { |
for (size_t i = 0; i < data_packs_.size(); ++i) { |
if (data_packs_[i]->GetScaleFactor() == scale_factor) { |
- SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); |
- if (bitmap) |
- return bitmap; |
+ if (LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) |
+ return true; |
} |
} |
- return NULL; |
+ return false; |
} |
gfx::Image& ResourceBundle::GetEmptyImage() { |