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

Side by Side Diff: chrome/browser/web_applications/web_app_mac.mm

Issue 2404793002: Mac: Write gfx::ImageFamily using CGImageDestination. (Closed)
Patch Set: WriteIconsToFile Created 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #import "chrome/browser/web_applications/web_app_mac.h" 5 #import "chrome/browser/web_applications/web_app_mac.h"
6 6
7 #import <Carbon/Carbon.h>
8 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
9 #include <stdint.h> 8 #include <stdint.h>
10 9
11 #include <utility> 10 #include <utility>
12 11
13 #include "base/command_line.h" 12 #include "base/command_line.h"
14 #include "base/files/file_enumerator.h" 13 #include "base/files/file_enumerator.h"
15 #include "base/files/file_util.h" 14 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h" 15 #include "base/files/scoped_temp_dir.h"
17 #include "base/mac/foundation_util.h" 16 #include "base/mac/foundation_util.h"
(...skipping 29 matching lines...) Expand all
47 #include "chrome/grit/generated_resources.h" 46 #include "chrome/grit/generated_resources.h"
48 #include "components/crx_file/id_util.h" 47 #include "components/crx_file/id_util.h"
49 #include "components/version_info/version_info.h" 48 #include "components/version_info/version_info.h"
50 #include "content/public/browser/browser_thread.h" 49 #include "content/public/browser/browser_thread.h"
51 #include "content/public/common/content_switches.h" 50 #include "content/public/common/content_switches.h"
52 #include "extensions/browser/extension_registry.h" 51 #include "extensions/browser/extension_registry.h"
53 #include "extensions/common/extension.h" 52 #include "extensions/common/extension.h"
54 #import "skia/ext/skia_utils_mac.h" 53 #import "skia/ext/skia_utils_mac.h"
55 #include "third_party/skia/include/core/SkBitmap.h" 54 #include "third_party/skia/include/core/SkBitmap.h"
56 #include "third_party/skia/include/core/SkColor.h" 55 #include "third_party/skia/include/core/SkColor.h"
56 #include "third_party/skia/include/utils/mac/SkCGUtils.h"
57 #include "ui/base/l10n/l10n_util.h" 57 #include "ui/base/l10n/l10n_util.h"
58 #include "ui/base/resource/resource_bundle.h" 58 #include "ui/base/resource/resource_bundle.h"
59 #include "ui/gfx/image/image_family.h" 59 #include "ui/gfx/image/image_family.h"
60 60
61 bool g_app_shims_allow_update_and_launch_in_tests = false; 61 bool g_app_shims_allow_update_and_launch_in_tests = false;
62 62
63 namespace { 63 namespace {
64 64
65 // Launch Services Key to run as an agent app, which doesn't launch in the dock. 65 // Launch Services Key to run as an agent app, which doesn't launch in the dock.
66 NSString* const kLSUIElement = @"LSUIElement"; 66 NSString* const kLSUIElement = @"LSUIElement";
(...skipping 19 matching lines...) Expand all
86 content::BrowserThread::UI>; 86 content::BrowserThread::UI>;
87 87
88 ~Latch() { callback_.Run(); } 88 ~Latch() { callback_.Run(); }
89 void NoOp() {} 89 void NoOp() {}
90 90
91 base::Closure callback_; 91 base::Closure callback_;
92 92
93 DISALLOW_COPY_AND_ASSIGN(Latch); 93 DISALLOW_COPY_AND_ASSIGN(Latch);
94 }; 94 };
95 95
96 class ScopedCarbonHandle { 96 // Writes |icons| to |path| in .icns format.
97 public: 97 bool WriteIconsToFile(const std::vector<gfx::Image>& icons,
98 ScopedCarbonHandle(size_t initial_size) : handle_(NewHandle(initial_size)) { 98 const base::FilePath& path) {
99 DCHECK(handle_); 99 base::scoped_nsobject<NSMutableData> data(
100 DCHECK_EQ(noErr, MemError()); 100 [[NSMutableData alloc] initWithCapacity:0]);
101 base::ScopedCFTypeRef<CGImageDestinationRef> image_destination(
102 CGImageDestinationCreateWithData(base::mac::NSToCFCast(data),
103 kUTTypeAppleICNS, icons.size(),
104 nullptr));
105 DCHECK(image_destination);
106 for (const gfx::Image& image : icons) {
107 base::ScopedCFTypeRef<CGImageRef> cg_image(SkCreateCGImageRefWithColorspace(
108 image.AsBitmap(), base::mac::GetSRGBColorSpace()));
109 CGImageDestinationAddImage(image_destination, cg_image, nullptr);
101 } 110 }
102 ~ScopedCarbonHandle() { DisposeHandle(handle_); } 111 if (!CGImageDestinationFinalize(image_destination)) {
103 112 NOTREACHED() << "CGImageDestinationFinalize failed.";
104 Handle Get() { return handle_; } 113 return false;
105 char* Data() { return *handle_; }
106 size_t HandleSize() const { return GetHandleSize(handle_); }
107
108 IconFamilyHandle GetAsIconFamilyHandle() {
109 return reinterpret_cast<IconFamilyHandle>(handle_);
110 } 114 }
111 115 return [data writeToFile:base::mac::FilePathToNSString(path) atomically:NO];
112 bool WriteDataToFile(const base::FilePath& path) {
113 NSData* data = [NSData dataWithBytes:Data()
114 length:HandleSize()];
115 return [data writeToFile:base::mac::FilePathToNSString(path)
116 atomically:NO];
117 }
118
119 private:
120 Handle handle_;
121 };
122
123 void ConvertSkiaToARGB(const SkBitmap& bitmap, ScopedCarbonHandle* handle) {
124 CHECK_EQ(4u * bitmap.width() * bitmap.height(), handle->HandleSize());
125
126 char* argb = handle->Data();
127 SkAutoLockPixels lock(bitmap);
128 for (int y = 0; y < bitmap.height(); ++y) {
129 for (int x = 0; x < bitmap.width(); ++x) {
130 SkColor pixel = bitmap.getColor(x, y);
131 argb[0] = SkColorGetA(pixel);
132 argb[1] = SkColorGetR(pixel);
133 argb[2] = SkColorGetG(pixel);
134 argb[3] = SkColorGetB(pixel);
135 argb += 4;
136 }
137 }
138 } 116 }
139 117
140 // Adds |image| to |icon_family|. Returns true on success, false on failure. 118 // Returns true if |image| can be used for an icon resource.
141 bool AddGfxImageToIconFamily(IconFamilyHandle icon_family, 119 bool IsImageValidForIcon(const gfx::Image& image) {
142 const gfx::Image& image) { 120 if (image.IsEmpty())
121 return false;
122
143 // When called via ShowCreateChromeAppShortcutsDialog the ImageFamily will 123 // When called via ShowCreateChromeAppShortcutsDialog the ImageFamily will
144 // have all the representations desired here for mac, from the kDesiredSizes 124 // have all the representations desired here for mac, from the kDesiredSizes
145 // array in web_app.cc. 125 // array in web_app.cc.
146 SkBitmap bitmap = image.AsBitmap(); 126 SkBitmap bitmap = image.AsBitmap();
147 if (bitmap.colorType() != kN32_SkColorType || 127 if (bitmap.colorType() != kN32_SkColorType ||
148 bitmap.width() != bitmap.height()) { 128 bitmap.width() != bitmap.height()) {
149 return false; 129 return false;
150 } 130 }
151 131
152 OSType icon_type;
153 switch (bitmap.width()) { 132 switch (bitmap.width()) {
154 case 512: 133 case 512:
155 icon_type = kIconServices512PixelDataARGB;
156 break;
157 case 256: 134 case 256:
158 icon_type = kIconServices256PixelDataARGB;
159 break;
160 case 128: 135 case 128:
161 icon_type = kIconServices128PixelDataARGB;
162 break;
163 case 48: 136 case 48:
164 icon_type = kIconServices48PixelDataARGB;
165 break;
166 case 32: 137 case 32:
167 icon_type = kIconServices32PixelDataARGB;
168 break;
169 case 16: 138 case 16:
170 icon_type = kIconServices16PixelDataARGB; 139 return true;
171 break;
172 default:
173 return false;
174 } 140 }
175 141 return false;
176 ScopedCarbonHandle raw_data(bitmap.getSize());
177 ConvertSkiaToARGB(bitmap, &raw_data);
178 OSErr result = SetIconFamilyData(icon_family, icon_type, raw_data.Get());
179 DCHECK_EQ(noErr, result);
180 return result == noErr;
181 } 142 }
182 143
183 bool AppShimsDisabledForTest() { 144 bool AppShimsDisabledForTest() {
184 // Disable app shims in tests because shims created in ~/Applications will not 145 // Disable app shims in tests because shims created in ~/Applications will not
185 // be cleaned up. 146 // be cleaned up.
186 return base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType); 147 return base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType);
187 } 148 }
188 149
189 base::FilePath GetWritableApplicationsDirectory() { 150 base::FilePath GetWritableApplicationsDirectory() {
190 base::FilePath path; 151 base::FilePath path;
(...skipping 778 matching lines...) Expand 10 before | Expand all | Expand 10 after
969 NSString* localized_path = base::mac::FilePathToNSString( 930 NSString* localized_path = base::mac::FilePathToNSString(
970 localized_dir.Append("InfoPlist.strings")); 931 localized_dir.Append("InfoPlist.strings"));
971 return [strings_plist writeToFile:localized_path 932 return [strings_plist writeToFile:localized_path
972 atomically:YES]; 933 atomically:YES];
973 } 934 }
974 935
975 bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const { 936 bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const {
976 if (info_->favicon.empty()) 937 if (info_->favicon.empty())
977 return true; 938 return true;
978 939
979 ScopedCarbonHandle icon_family(0); 940 std::vector<gfx::Image> valid_icons;
980 bool image_added = false;
981 for (gfx::ImageFamily::const_iterator it = info_->favicon.begin(); 941 for (gfx::ImageFamily::const_iterator it = info_->favicon.begin();
982 it != info_->favicon.end(); ++it) { 942 it != info_->favicon.end(); ++it) {
983 if (it->IsEmpty()) 943 if (IsImageValidForIcon(*it))
984 continue; 944 valid_icons.push_back(*it);
985
986 // Missing an icon size is not fatal so don't fail if adding the bitmap
987 // doesn't work.
988 if (!AddGfxImageToIconFamily(icon_family.GetAsIconFamilyHandle(), *it))
989 continue;
990
991 image_added = true;
992 } 945 }
993 946 if (valid_icons.empty())
994 if (!image_added)
995 return false; 947 return false;
996 948
997 base::FilePath resources_path = GetResourcesPath(app_path); 949 base::FilePath resources_path = GetResourcesPath(app_path);
998 if (!base::CreateDirectory(resources_path)) 950 if (!base::CreateDirectory(resources_path))
999 return false; 951 return false;
1000 952
1001 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); 953 return WriteIconsToFile(valid_icons, resources_path.Append("app.icns"));
1002 } 954 }
1003 955
1004 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { 956 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const {
1005 NSString* plist_path = GetPlistPath(GetInternalShortcutPath()); 957 NSString* plist_path = GetPlistPath(GetInternalShortcutPath());
1006 NSMutableDictionary* plist = ReadPlist(plist_path); 958 NSMutableDictionary* plist = ReadPlist(plist_path);
1007 959
1008 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) 960 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier())
1009 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; 961 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)];
1010 return [plist writeToFile:plist_path 962 return [plist writeToFile:plist_path
1011 atomically:YES]; 963 atomically:YES];
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 BuildShortcutInfoFromBundle(*it); 1129 BuildShortcutInfoFromBundle(*it);
1178 WebAppShortcutCreator shortcut_creator(it->DirName(), shortcut_info.get(), 1130 WebAppShortcutCreator shortcut_creator(it->DirName(), shortcut_info.get(),
1179 extensions::FileHandlersInfo()); 1131 extensions::FileHandlersInfo());
1180 shortcut_creator.DeleteShortcuts(); 1132 shortcut_creator.DeleteShortcuts();
1181 } 1133 }
1182 } 1134 }
1183 1135
1184 } // namespace internals 1136 } // namespace internals
1185 1137
1186 } // namespace web_app 1138 } // namespace web_app
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698