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/files/file_path.h" 11#include "base/files/file_util.h" 12#include "base/mac/bundle_locations.h" 13#include "base/mac/foundation_util.h" 14#include "base/mac/scoped_nsobject.h" 15#include "base/memory/ref_counted_memory.h" 16#include "base/notreached.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 22namespace ui { 23 24namespace { 25 26base::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 46void ResourceBundle::LoadCommonResources() { 47 if (IsScaleFactorSupported(SCALE_FACTOR_100P)) { 48 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_100_percent", nil), 49 SCALE_FACTOR_100P); 50 } 51 52 if (IsScaleFactorSupported(SCALE_FACTOR_200P)) { 53 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_200_percent", nil), 54 SCALE_FACTOR_200P); 55 } 56 57 if (IsScaleFactorSupported(SCALE_FACTOR_300P)) { 58 AddDataPackFromPath(GetResourcesPakFilePath(@"chrome_300_percent", nil), 59 SCALE_FACTOR_300P); 60 } 61} 62 63// static 64base::FilePath ResourceBundle::GetLocaleFilePath( 65 const std::string& app_locale) { 66 NSString* mac_locale = base::SysUTF8ToNSString(app_locale); 67 68 // iOS uses "_" instead of "-", so swap to get a iOS-style value. 69 mac_locale = [mac_locale stringByReplacingOccurrencesOfString:@"-" 70 withString:@"_"]; 71 72 // On disk, the "en_US" resources are just "en" (http://crbug.com/25578). 73 if ([mac_locale isEqual:@"en_US"]) 74 mac_locale = @"en"; 75 76 base::FilePath locale_file_path = 77 GetResourcesPakFilePath(@"locale", mac_locale); 78 79 if (HasSharedInstance() && GetSharedInstance().delegate_) { 80 locale_file_path = GetSharedInstance().delegate_->GetPathForLocalePack( 81 locale_file_path, app_locale); 82 } 83 84 // Don't try to load from paths that are not absolute. 85 return locale_file_path.IsAbsolute() ? locale_file_path : base::FilePath(); 86} 87 88gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { 89 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 90 91 // Check to see if the image is already in the cache. 92 ImageMap::iterator found = images_.find(resource_id); 93 if (found != images_.end()) { 94 return found->second; 95 } 96 97 gfx::Image image; 98 if (delegate_) 99 image = delegate_->GetNativeImageNamed(resource_id); 100 101 if (image.IsEmpty()) { 102 // Load the raw data from the resource pack at the current supported scale 103 // factor. This code assumes that only one of the possible scale factors is 104 // supported at runtime, based on the device resolution. 105 ui::ScaleFactor scale_factor = GetMaxScaleFactor(); 106 107 scoped_refptr<base::RefCountedMemory> data( 108 LoadDataResourceBytesForScale(resource_id, scale_factor)); 109 110 if (!data.get()) { 111 LOG(WARNING) << "Unable to load image with id " << resource_id; 112 return GetEmptyImage(); 113 } 114 115 // Create a data object from the raw bytes. 116 base::scoped_nsobject<NSData> ns_data( 117 [[NSData alloc] initWithBytes:data->front() length:data->size()]); 118 119 bool is_fallback = PNGContainsFallbackMarker(data->front(), data->size()); 120 // Create the image from the data. 121 CGFloat target_scale = ui::GetScaleForScaleFactor(scale_factor); 122 CGFloat source_scale = is_fallback ? 1.0 : target_scale; 123 base::scoped_nsobject<UIImage> ui_image( 124 [[UIImage alloc] initWithData:ns_data scale:source_scale]); 125 126 // If the image is a 1x fallback, scale it up to a full-size representation. 127 if (is_fallback) { 128 CGSize source_size = [ui_image size]; 129 CGSize target_size = CGSizeMake(source_size.width * target_scale, 130 source_size.height * target_scale); 131 base::ScopedCFTypeRef<CGColorSpaceRef> color_space( 132 CGColorSpaceCreateDeviceRGB()); 133 base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( 134 NULL, 135 target_size.width, 136 target_size.height, 137 8, 138 target_size.width * 4, 139 color_space, 140 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); 141 142 CGRect target_rect = CGRectMake(0, 0, 143 target_size.width, target_size.height); 144 CGContextSetBlendMode(context, kCGBlendModeCopy); 145 CGContextDrawImage(context, target_rect, [ui_image CGImage]); 146 147 base::ScopedCFTypeRef<CGImageRef> cg_image( 148 CGBitmapContextCreateImage(context)); 149 ui_image.reset([[UIImage alloc] initWithCGImage:cg_image 150 scale:target_scale 151 orientation:UIImageOrientationUp]); 152 } 153 154 if (!ui_image.get()) { 155 LOG(WARNING) << "Unable to load image with id " << resource_id; 156 NOTREACHED(); // Want to assert in debug mode. 157 return GetEmptyImage(); 158 } 159 160 image = gfx::Image(ui_image); 161 } 162 163 auto inserted = images_.emplace(resource_id, image); 164 DCHECK(inserted.second); 165 return inserted.first->second; 166} 167 168} // namespace ui 169