1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/base/resource/resource_bundle.h" 6 7 #import <QuartzCore/QuartzCore.h> 8 #import <UIKit/UIKit.h> 9 10 #include "base/basictypes.h" 11 #include "base/file_util.h" 12 #include "base/files/file_path.h" 13 #include "base/mac/bundle_locations.h" 14 #include "base/mac/foundation_util.h" 15 #include "base/mac/scoped_nsobject.h" 16 #include "base/memory/ref_counted_memory.h" 17 #include "base/strings/sys_string_conversions.h" 18 #include "base/synchronization/lock.h" 19 #include "ui/base/resource/resource_handle.h" 20 #include "ui/gfx/image/image.h" 21 22 namespace ui { 23 24 namespace { 25 26 base::FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) { 27 NSString *resource_path; 28 if ([mac_locale length]) { 29 resource_path = [base::mac::FrameworkBundle() pathForResource:name 30 ofType:@"pak" 31 inDirectory:@"" 32 forLocalization:mac_locale]; 33 } else { 34 resource_path = [base::mac::FrameworkBundle() pathForResource:name 35 ofType:@"pak"]; 36 } 37 if (!resource_path) { 38 // Return just the name of the pak file. 39 return base::FilePath(base::SysNSStringToUTF8(name) + ".pak"); 40 } 41 return base::FilePath([resource_path fileSystemRepresentation]); 42 } 43 44 } // namespace 45 46 void ResourceBundle::LoadCommonResources() { 47 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome", nil), 48 ui::SCALE_FACTOR_NONE); 49 50 if (IsScaleFactorSupported(SCALE_FACTOR_100P)) { 51 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent", nil), 52 SCALE_FACTOR_100P); 53 } 54 55 if (IsScaleFactorSupported(SCALE_FACTOR_200P)) { 56 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil), 57 SCALE_FACTOR_200P); 58 } 59 } 60 61 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, 62 bool test_file_exists) { 63 NSString* mac_locale = base::SysUTF8ToNSString(app_locale); 64 65 // iOS uses "_" instead of "-", so swap to get a iOS-style value. 66 mac_locale = [mac_locale stringByReplacingOccurrencesOfString:@"-" 67 withString:@"_"]; 68 69 // On disk, the "en_US" resources are just "en" (http://crbug.com/25578). 70 if ([mac_locale isEqual:@"en_US"]) 71 mac_locale = @"en"; 72 73 base::FilePath locale_file_path = 74 GetResourcesPakFilePath(@"locale", mac_locale); 75 76 if (delegate_) { 77 locale_file_path = 78 delegate_->GetPathForLocalePack(locale_file_path, app_locale); 79 } 80 81 // Don't try to load empty values or values that are not absolute paths. 82 if (locale_file_path.empty() || !locale_file_path.IsAbsolute()) 83 return base::FilePath(); 84 85 if (test_file_exists && !base::PathExists(locale_file_path)) 86 return base::FilePath(); 87 88 return locale_file_path; 89 } 90 91 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { 92 // Flipped images are not used on iOS. 93 DCHECK_EQ(rtl, RTL_DISABLED); 94 95 // Check to see if the image is already in the cache. 96 { 97 base::AutoLock lock(*images_and_fonts_lock_); 98 ImageMap::iterator found = images_.find(resource_id); 99 if (found != images_.end()) { 100 return found->second; 101 } 102 } 103 104 gfx::Image image; 105 if (delegate_) 106 image = delegate_->GetNativeImageNamed(resource_id, rtl); 107 108 if (image.IsEmpty()) { 109 // Load the raw data from the resource pack at the current supported scale 110 // factor. This code assumes that only one of the possible scale factors is 111 // supported at runtime, based on the device resolution. 112 ui::ScaleFactor scale_factor = GetMaxScaleFactor(); 113 114 scoped_refptr<base::RefCountedStaticMemory> data( 115 LoadDataResourceBytesForScale(resource_id, scale_factor)); 116 117 if (!data.get()) { 118 LOG(WARNING) << "Unable to load image with id " << resource_id; 119 return GetEmptyImage(); 120 } 121 122 // Create a data object from the raw bytes. 123 base::scoped_nsobject<NSData> ns_data( 124 [[NSData alloc] initWithBytes:data->front() length:data->size()]); 125 126 bool is_fallback = PNGContainsFallbackMarker(data->front(), data->size()); 127 // Create the image from the data. 128 CGFloat target_scale = ui::GetImageScale(scale_factor); 129 CGFloat source_scale = is_fallback ? 1.0 : target_scale; 130 base::scoped_nsobject<UIImage> ui_image( 131 [[UIImage alloc] initWithData:ns_data scale:source_scale]); 132 133 // If the image is a 1x fallback, scale it up to a full-size representation. 134 if (is_fallback) { 135 CGSize source_size = [ui_image size]; 136 CGSize target_size = CGSizeMake(source_size.width * target_scale, 137 source_size.height * target_scale); 138 base::ScopedCFTypeRef<CGColorSpaceRef> color_space( 139 CGColorSpaceCreateDeviceRGB()); 140 base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( 141 NULL, 142 target_size.width, 143 target_size.height, 144 8, 145 target_size.width * 4, 146 color_space, 147 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); 148 149 CGRect target_rect = CGRectMake(0, 0, 150 target_size.width, target_size.height); 151 CGContextSetBlendMode(context, kCGBlendModeCopy); 152 CGContextDrawImage(context, target_rect, [ui_image CGImage]); 153 154 if (ShouldHighlightMissingScaledResources()) { 155 CGContextSetFillColorSpace(context, color_space); 156 CGFloat components[4] = { 1.0, 0.0, 0.0, 0.3 }; // Translucent red. 157 CGContextSetFillColor(context, components); 158 CGContextSetBlendMode(context, kCGBlendModeNormal); 159 CGContextFillRect(context, target_rect); 160 } 161 162 base::ScopedCFTypeRef<CGImageRef> cg_image( 163 CGBitmapContextCreateImage(context)); 164 ui_image.reset([[UIImage alloc] initWithCGImage:cg_image 165 scale:target_scale 166 orientation:UIImageOrientationUp]); 167 } 168 169 if (!ui_image.get()) { 170 LOG(WARNING) << "Unable to load image with id " << resource_id; 171 NOTREACHED(); // Want to assert in debug mode. 172 return GetEmptyImage(); 173 } 174 175 // The gfx::Image takes ownership. 176 image = gfx::Image(ui_image.release()); 177 } 178 179 base::AutoLock lock(*images_and_fonts_lock_); 180 181 // Another thread raced the load and has already cached the image. 182 if (images_.count(resource_id)) 183 return images_[resource_id]; 184 185 images_[resource_id] = image; 186 return images_[resource_id]; 187 } 188 189 } // namespace ui 190