15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#import <QuartzCore/QuartzCore.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#import <UIKit/UIKit.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/bundle_locations.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/foundation_util.h" 15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/mac/scoped_nsobject.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted_memory.h" 17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h" 18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/synchronization/lock.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_handle.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ui { 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSString *resource_path; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ([mac_locale length]) { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) resource_path = [base::mac::FrameworkBundle() pathForResource:name 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ofType:@"pak" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) inDirectory:@"" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) forLocalization:mac_locale]; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) resource_path = [base::mac::FrameworkBundle() pathForResource:name 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ofType:@"pak"]; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!resource_path) { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Return just the name of the pak file. 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(base::SysNSStringToUTF8(name) + ".pak"); 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath([resource_path fileSystemRepresentation]); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ResourceBundle::LoadCommonResources() { 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddDataPackFromPath(GetResourcesPakFilePath(@"chrome", nil), 48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ui::SCALE_FACTOR_NONE); 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsScaleFactorSupported(SCALE_FACTOR_100P)) { 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent", nil), 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SCALE_FACTOR_100P); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsScaleFactorSupported(SCALE_FACTOR_200P)) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil), 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SCALE_FACTOR_200P); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(rohitrao): Add a chrome_300_percent file and load it here. For now, 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // we are simply falling back to the 200P resources. http://crbug.com/413300. 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (IsScaleFactorSupported(SCALE_FACTOR_300P)) { 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil), 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci SCALE_FACTOR_200P); 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool test_file_exists) { 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSString* mac_locale = base::SysUTF8ToNSString(app_locale); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // iOS uses "_" instead of "-", so swap to get a iOS-style value. 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mac_locale = [mac_locale stringByReplacingOccurrencesOfString:@"-" 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) withString:@"_"]; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // On disk, the "en_US" resources are just "en" (http://crbug.com/25578). 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ([mac_locale isEqual:@"en_US"]) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mac_locale = @"en"; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath locale_file_path = 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) GetResourcesPakFilePath(@"locale", mac_locale); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (delegate_) { 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) locale_file_path = 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delegate_->GetPathForLocalePack(locale_file_path, app_locale); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't try to load empty values or values that are not absolute paths. 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (locale_file_path.empty() || !locale_file_path.IsAbsolute()) 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (test_file_exists && !base::PathExists(locale_file_path)) 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return locale_file_path; 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Flipped images are not used on iOS. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(rtl, RTL_DISABLED); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check to see if the image is already in the cache. 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::AutoLock lock(*images_and_fonts_lock_); 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ImageMap::iterator found = images_.find(resource_id); 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (found != images_.end()) { 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return found->second; 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::Image image; 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (delegate_) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) image = delegate_->GetNativeImageNamed(resource_id, rtl); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (image.IsEmpty()) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Load the raw data from the resource pack at the current supported scale 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // factor. This code assumes that only one of the possible scale factors is 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // supported at runtime, based on the device resolution. 11968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) ui::ScaleFactor scale_factor = GetMaxScaleFactor(); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<base::RefCountedStaticMemory> data( 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LoadDataResourceBytesForScale(resource_id, scale_factor)); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!data.get()) { 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) LOG(WARNING) << "Unable to load image with id " << resource_id; 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return GetEmptyImage(); 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create a data object from the raw bytes. 130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::scoped_nsobject<NSData> ns_data( 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [[NSData alloc] initWithBytes:data->front() length:data->size()]); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool is_fallback = PNGContainsFallbackMarker(data->front(), data->size()); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Create the image from the data. 135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) CGFloat target_scale = ui::GetScaleForScaleFactor(scale_factor); 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Hack: The 200P pak file is the only pak file loaded on iOS devices with 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // an @3x scale factor. Force |source_scale| to be 2.0 to handle this case, 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // since it cannot be anything else. http://crbug.com/413300. 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(rohitrao): Support proper fallback by using the actual scale factor 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // of the source image, rather than assuming it is 1.0 or 2.0. 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CGFloat source_scale = target_scale; 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (is_fallback) { 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci source_scale = 1.0; 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (scale_factor == SCALE_FACTOR_300P) { 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci source_scale = 2.0; 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::scoped_nsobject<UIImage> ui_image( 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) [[UIImage alloc] initWithData:ns_data scale:source_scale]); 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If the image is a 1x fallback, scale it up to a full-size representation. 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (is_fallback) { 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGSize source_size = [ui_image size]; 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGSize target_size = CGSizeMake(source_size.width * target_scale, 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) source_size.height * target_scale); 155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::ScopedCFTypeRef<CGColorSpaceRef> color_space( 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGColorSpaceCreateDeviceRGB()); 157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( 158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch NULL, 159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch target_size.width, 160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch target_size.height, 161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 8, 162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch target_size.width * 4, 163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch color_space, 164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGRect target_rect = CGRectMake(0, 0, 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) target_size.width, target_size.height); 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGContextSetBlendMode(context, kCGBlendModeCopy); 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGContextDrawImage(context, target_rect, [ui_image CGImage]); 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch base::ScopedCFTypeRef<CGImageRef> cg_image( 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CGBitmapContextCreateImage(context)); 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ui_image.reset([[UIImage alloc] initWithCGImage:cg_image 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scale:target_scale 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) orientation:UIImageOrientationUp]); 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ui_image.get()) { 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to load image with id " << resource_id; 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); // Want to assert in debug mode. 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetEmptyImage(); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // The gfx::Image takes ownership. 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) image = gfx::Image(ui_image.release()); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::AutoLock lock(*images_and_fonts_lock_); 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Another thread raced the load and has already cached the image. 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (images_.count(resource_id)) 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return images_[resource_id]; 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) images_[resource_id] = image; 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return images_[resource_id]; 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace ui 199