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

Side by Side Diff: ios/chrome/browser/ui/uikit_ui_util.mm

Issue 802633007: Upstream iOS UI utilities. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 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
OLDNEW
(Empty)
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 #import "ios/chrome/browser/ui/uikit_ui_util.h"
6
7 #import <Accelerate/Accelerate.h>
8 #import <Foundation/Foundation.h>
9 #import <QuartzCore/QuartzCore.h>
10 #import <UIKit/UIKit.h>
11 #include <cmath>
12
13 #include "base/logging.h"
14 #include "base/ios/ios_util.h"
15 #include "base/mac/foundation_util.h"
16 #include "ios/chrome/browser/ui/ui_util.h"
17 #include "ios/chrome/browser/ui/ui_util.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/l10n/l10n_util_mac.h"
20 #include "ui/gfx/scoped_cg_context_save_gstate_mac.h"
21 #include "ui/ios/uikit_util.h"
22
23 namespace {
24
25 // Linearly interpolate between |a| and |b| by fraction |f|. Satisfies
26 // |lerp(a,b,0) == a| and |lerp(a,b,1) == b|.
sdefresne 2015/01/13 15:19:25 nit: s/lerp/Lerp/g
27 CGFloat Lerp(CGFloat a, CGFloat b, CGFloat fraction) {
28 return a * (1.0f - fraction) + b * fraction;
29 }
30
31 // Gets the RGBA components from a UIColor in RBG or monochrome color space.
32 void GetRGBA(UIColor* color, CGFloat* r, CGFloat* g, CGFloat* b, CGFloat* a) {
33 switch (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor))) {
34 case kCGColorSpaceModelRGB: {
35 BOOL success = [color getRed:r green:g blue:b alpha:a];
36 DCHECK(success);
37 return;
38 }
39 case kCGColorSpaceModelMonochrome: {
40 const size_t componentsCount =
41 CGColorGetNumberOfComponents(color.CGColor);
42 DCHECK(componentsCount == 1 || componentsCount == 2);
43 const CGFloat* components = CGColorGetComponents(color.CGColor);
44 *r = components[0];
45 *g = components[0];
46 *b = components[0];
47 *a = componentsCount == 1 ? 1 : components[1];
48 return;
49 }
50 default:
51 NOTREACHED() << "Unsupported color space.";
52 return;
53 }
54 }
55
56 } // namespace
57
58 void SetA11yLabelAndUiAutomationName(UIView* element,
59 int idsAccessibilityLabel,
60 NSString* englishUiAutomationName) {
61 [element setAccessibilityLabel:l10n_util::GetNSString(idsAccessibilityLabel)];
62 [element setAccessibilityIdentifier:englishUiAutomationName];
63 }
64
65 void GetSizeButtonWidthToFit(UIButton* button) {
66 // Resize the button's width to fit the new text, but keep the original
67 // height. sizeToFit appears to ignore the image size, so re-add the size of
68 // the button's image to the frame width.
69 CGFloat buttonHeight = [button frame].size.height;
70 CGFloat imageWidth = [[button imageView] frame].size.width;
71 [button sizeToFit];
72 CGRect newFrame = [button frame];
73 newFrame.size.height = buttonHeight;
74 newFrame.size.width += imageWidth;
75 [button setFrame:newFrame];
76 }
77
78 void TranslateFrame(UIView* view, UIOffset offset) {
79 if (!view)
80 return;
81
82 CGRect frame = [view frame];
83 frame.origin.x = frame.origin.x + offset.horizontal;
84 frame.origin.y = frame.origin.y + offset.vertical;
85 [view setFrame:frame];
86 }
87
88 UIFont* GetUIFont(int fontFace, bool isBold, CGFloat fontSize) {
89 NSString* fontFaceName;
90 switch (fontFace) {
91 case FONT_HELVETICA:
92 fontFaceName = isBold ? @"Helvetica-Bold" : @"Helvetica";
93 break;
94 case FONT_HELVETICA_NEUE:
95 fontFaceName = isBold ? @"HelveticaNeue-Bold" : @"HelveticaNeue";
96 break;
97 case FONT_HELVETICA_NEUE_LIGHT:
98 // FONT_HELVETICA_NEUE_LIGHT does not support Bold.
99 DCHECK(!isBold);
100 fontFaceName = @"HelveticaNeue-Light";
101 break;
102 default:
103 NOTREACHED();
104 fontFaceName = @"Helvetica";
105 break;
106 }
107 return [UIFont fontWithName:fontFaceName size:fontSize];
108 }
109
110 void AddBorderShadow(UIView* view, CGFloat offset, UIColor* color) {
111 CGRect rect = CGRectInset(view.bounds, -offset, -offset);
112 CGPoint waypoints[] = {
113 CGPointMake(rect.origin.x, rect.origin.y),
114 CGPointMake(rect.origin.x, rect.origin.y + rect.size.height),
115 CGPointMake(rect.origin.x + rect.size.width,
116 rect.origin.y + rect.size.height),
117 CGPointMake(rect.origin.x + rect.size.width, rect.origin.y),
118 CGPointMake(rect.origin.x, rect.origin.y)};
119 int numberOfWaypoints = sizeof(waypoints) / sizeof(waypoints[0]);
120 CGMutablePathRef outline = CGPathCreateMutable();
121 CGPathAddLines(outline, NULL, waypoints, numberOfWaypoints);
122 view.layer.shadowColor = [color CGColor];
123 view.layer.shadowOpacity = 1.0;
124 view.layer.shadowOffset = CGSizeZero;
125 view.layer.shadowPath = outline;
126 CGPathRelease(outline);
127 }
128
129 // TODO(pkl): The implementation of this has some duplicated code with
130 // AddBorderShadow and ToolsPopupView newPathForRect:withRadius:withArrow:.
131 // There is an opportunity to refactor them into a common shadow library.
132 void AddRoundedBorderShadow(UIView* view, CGFloat radius, UIColor* color) {
133 CGRect rect = view.bounds;
134 CGMutablePathRef path = CGPathCreateMutable();
135 CGFloat minX = CGRectGetMinX(rect);
136 CGFloat midX = CGRectGetMidX(rect);
137 CGFloat maxX = CGRectGetMaxX(rect);
138 CGFloat minY = CGRectGetMinY(rect);
139 CGFloat midY = CGRectGetMidY(rect);
140 CGFloat maxY = CGRectGetMaxY(rect);
141 CGPathMoveToPoint(path, NULL, minX, midY);
142 CGPathAddArcToPoint(path, NULL, minX, minY, midX, minY, radius);
143 CGPathAddArcToPoint(path, NULL, maxX, minY, maxX, midY, radius);
144 CGPathAddArcToPoint(path, NULL, maxX, maxY, midX, maxY, radius);
145 CGPathAddArcToPoint(path, NULL, minX, maxY, minX, midY, radius);
146 CGPathCloseSubpath(path);
147 view.layer.shadowColor = [color CGColor];
148 view.layer.shadowOpacity = 1.0;
149 view.layer.shadowRadius = radius;
150 view.layer.shadowOffset = CGSizeZero;
151 view.layer.shadowPath = path;
152 view.layer.borderWidth = 0.0;
153 CGPathRelease(path);
154 }
155
156 UIImage* CaptureView(UIView* view, CGFloat scale) {
157 UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES /* opaque */,
158 scale);
159 CGContext* context = UIGraphicsGetCurrentContext();
160 [view.layer renderInContext:context];
161 UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
162 UIGraphicsEndImageContext();
163 return image;
164 }
165
166 UIImage* GreyImage(UIImage* image) {
167 DCHECK(image);
168 // Grey images are always non-retina to improve memory performance.
169 UIGraphicsBeginImageContextWithOptions(image.size, YES, 1.0);
170 CGRect greyImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
171 [image drawInRect:greyImageRect blendMode:kCGBlendModeLuminosity alpha:1.0];
172 UIImage* greyImage = UIGraphicsGetImageFromCurrentImageContext();
173 UIGraphicsEndImageContext();
174 return greyImage;
175 }
176
177 UIColor* GetPrimaryActionButtonColor() {
178 return UIColorFromRGB(0x2d6ada, 1.0);
179 }
180
181 UIColor* GetSettingsBackgroundColor() {
182 CGFloat rgb = 237.0 / 255.0;
183 return [UIColor colorWithWhite:rgb alpha:1];
184 }
185
186 UIImage* ResizeImage(UIImage* image,
187 CGSize targetSize,
188 BOOL preserveAspectRatio,
189 BOOL trimToFit) {
190 CGSize revisedTargetSize;
191 CGRect projectTo;
192
193 CalculateProjection([image size], targetSize, preserveAspectRatio, trimToFit,
194 revisedTargetSize, projectTo);
195
196 if (CGRectEqualToRect(projectTo, CGRectZero))
197 return nil;
198
199 // Resize photo. Use UIImage drawing methods because they respect
200 // UIImageOrientation as opposed to CGContextDrawImage().
201 UIGraphicsBeginImageContextWithOptions(revisedTargetSize, NO, image.scale);
202 [image drawInRect:projectTo];
203 UIImage* resizedPhoto = UIGraphicsGetImageFromCurrentImageContext();
204 UIGraphicsEndImageContext();
205 return resizedPhoto;
206 }
207
208 UIImage* DarkenImage(UIImage* image) {
209 UIColor* tintColor = [UIColor colorWithWhite:0.22 alpha:0.6];
210 return BlurImage(image,
211 3.0, // blurRadius,
212 tintColor,
213 1.8, // saturationDeltaFactor
214 nil);
215 }
216
217 UIImage* BlurImage(UIImage* image,
218 CGFloat blurRadius,
219 UIColor* tintColor,
220 CGFloat saturationDeltaFactor,
221 UIImage* maskImage) {
222 // This code is heavily inspired by the UIImageEffect sample project,
223 // presented at WWDC and available from Apple.
224 DCHECK(image.size.width >= 1 && image.size.height >= 1);
225 DCHECK(image.CGImage);
226 DCHECK(!maskImage || maskImage.CGImage);
227
228 CGRect imageRect = {CGPointZero, image.size};
229 UIImage* effectImage = nil;
230
231 BOOL hasBlur = blurRadius > __FLT_EPSILON__;
232 BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
233 if (hasBlur || hasSaturationChange) {
234 UIGraphicsBeginImageContextWithOptions(image.size,
235 NO, // opaque.
236 [[UIScreen mainScreen] scale]);
237 CGContextRef effectInContext = UIGraphicsGetCurrentContext();
238 CGContextScaleCTM(effectInContext, 1.0, -1.0);
239 CGContextTranslateCTM(effectInContext, 0, -image.size.height);
240 CGContextDrawImage(effectInContext, imageRect, image.CGImage);
241
242 vImage_Buffer effectInBuffer;
243 effectInBuffer.data = CGBitmapContextGetData(effectInContext);
244 effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
245 effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
246 effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
247
248 UIGraphicsBeginImageContextWithOptions(image.size,
249 NO, // opaque.
250 [[UIScreen mainScreen] scale]);
251 CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
252 vImage_Buffer effectOutBuffer;
253 effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
254 effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
255 effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
256 effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
257
258 // Those are swapped as effects are applied.
259 vImage_Buffer* inBuffer = &effectInBuffer;
260 vImage_Buffer* outBuffer = &effectOutBuffer;
261
262 if (hasBlur) {
263 // A description of how to compute the box kernel width from the Gaussian
264 // radius (aka standard deviation) appears in the SVG spec:
265 // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
266 //
267 // For larger values of 's' (s >= 2.0), an approximation can be used:
268 // Three successive box-blurs build a piece-wise quadratic convolution
269 // kernel, which approximates the Gaussian kernel to within roughly 3%.
270 //
271 // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
272 //
273 // ... if d is odd, use three box-blurs of size 'd', centered on the
274 // output pixel.
275 //
276 CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
277 NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
278 if (radius % 2 != 1) {
279 // force radius to be odd so that the three box-blur methodology works.
280 radius += 1;
281 }
282 for (int i = 0; i < 3; ++i) {
283 vImageBoxConvolve_ARGB8888(inBuffer, // src.
284 outBuffer, // dst.
285 NULL, // tempBuffer.
286 0, // srcOffsetToROI_X.
287 0, // srcOffsetToROI_Y
288 radius, // kernel_height
289 radius, // kernel_width
290 0, // backgroundColor
291 kvImageEdgeExtend); // flags
292 vImage_Buffer* temp = inBuffer;
293 inBuffer = outBuffer;
294 outBuffer = temp;
295 }
296 }
297 if (hasSaturationChange) {
298 CGFloat s = saturationDeltaFactor;
299 CGFloat floatingPointSaturationMatrix[] = {
300 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
301 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
302 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
303 0, 0, 0, 1 };
304 const int32_t divisor = 256;
305 NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix) /
306 sizeof(floatingPointSaturationMatrix[0]);
307 int16_t saturationMatrix[matrixSize];
308 for (NSUInteger i = 0; i < matrixSize; ++i) {
309 saturationMatrix[i] =
310 (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
311 }
312 vImageMatrixMultiply_ARGB8888(inBuffer, outBuffer, saturationMatrix,
313 divisor, NULL, NULL, kvImageNoFlags);
314 }
315 if (outBuffer == &effectOutBuffer)
316 effectImage = UIGraphicsGetImageFromCurrentImageContext();
317 UIGraphicsEndImageContext();
318
319 if (!effectImage)
320 effectImage = UIGraphicsGetImageFromCurrentImageContext();
321 UIGraphicsEndImageContext();
322 }
323
324 // Set up output context.
325 UIGraphicsBeginImageContextWithOptions(image.size,
326 NO, // opaque
327 [[UIScreen mainScreen] scale]);
328 CGContextRef outputContext = UIGraphicsGetCurrentContext();
329 CGContextScaleCTM(outputContext, 1.0, -1.0);
330 CGContextTranslateCTM(outputContext, 0, -image.size.height);
331
332 // Draw base image.
333 CGContextDrawImage(outputContext, imageRect, image.CGImage);
334
335 // Draw effect image.
336 if (effectImage) {
337 gfx::ScopedCGContextSaveGState context(outputContext);
338 if (maskImage)
339 CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
340 CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
341 }
342
343 // Add in color tint.
344 if (tintColor) {
345 gfx::ScopedCGContextSaveGState context(outputContext);
346 CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
347 CGContextFillRect(outputContext, imageRect);
348 }
349
350 // Output image is ready.
351 UIImage* outputImage = UIGraphicsGetImageFromCurrentImageContext();
352 UIGraphicsEndImageContext();
353
354 return outputImage;
355 }
356
357 UIInterfaceOrientation GetInterfaceOrientation() {
358 return [[UIApplication sharedApplication] statusBarOrientation];
359 }
360
361 CGFloat CurrentKeyboardHeight(NSValue* keyboardFrameValue) {
362 CGSize keyboardSize = [keyboardFrameValue CGRectValue].size;
363 if (base::ios::IsRunningOnIOS8OrLater()) {
364 return keyboardSize.height;
365 } else {
366 return IsPortrait() ? keyboardSize.height : keyboardSize.width;
367 }
368 }
369
370 UIImage* ImageWithColor(UIColor* color) {
371 CGRect rect = CGRectMake(0, 0, 1, 1);
372 UIGraphicsBeginImageContext(rect.size);
373 CGContextRef context = UIGraphicsGetCurrentContext();
374 CGContextSetFillColorWithColor(context, [color CGColor]);
375 CGContextFillRect(context, rect);
376 UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
377 UIGraphicsEndImageContext();
378 return image;
379 }
380
381 UIImage* CircularImageFromImage(UIImage* image, CGFloat width) {
382 CGRect frame =
383 CGRectMakeAlignedAndCenteredAt(width / 2.0, width / 2.0, width);
384
385 UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0.0);
386 CGContextRef context = UIGraphicsGetCurrentContext();
387
388 CGContextBeginPath(context);
389 CGContextAddEllipseInRect(context, frame);
390 CGContextClosePath(context);
391 CGContextClip(context);
392
393 CGFloat scaleX = frame.size.width / image.size.width;
394 CGFloat scaleY = frame.size.height / image.size.height;
395 CGFloat scale = std::max(scaleX, scaleY);
396 CGContextScaleCTM(context, scale, scale);
397
398 [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
399
400 image = UIGraphicsGetImageFromCurrentImageContext();
401 UIGraphicsEndImageContext();
402
403 return image;
404 }
405
406 UIColor* InterpolateFromColorToColor(UIColor* firstColor,
407 UIColor* secondColor,
408 CGFloat fraction) {
409 DCHECK_LE(0.0, fraction);
410 DCHECK_LE(fraction, 1.0);
411 CGFloat r1, r2, g1, g2, b1, b2, a1, a2;
412 GetRGBA(firstColor, &r1, &g1, &b1, &a1);
413 GetRGBA(secondColor, &r2, &g2, &b2, &a2);
414 return [UIColor colorWithRed:Lerp(r1, r2, fraction)
415 green:Lerp(g1, g2, fraction)
416 blue:Lerp(b1, b2, fraction)
417 alpha:Lerp(a1, a2, fraction)];
418 }
419
420 void ApplyVisualConstraints(NSArray* constraints,
421 NSDictionary* subviewsDictionary,
422 UIView* parentView) {
423 for (NSString* constraint in constraints) {
424 DCHECK([constraint isKindOfClass:[NSString class]]);
425 [parentView
426 addConstraints:[NSLayoutConstraint
427 constraintsWithVisualFormat:constraint
428 options:0
429 metrics:nil
430 views:subviewsDictionary]];
431 }
432 }
433
434 void AddSameCenterXConstraint(UIView* parentView, UIView* subview) {
435 DCHECK_EQ(parentView, [subview superview]);
436 [parentView addConstraint:[NSLayoutConstraint
437 constraintWithItem:subview
438 attribute:NSLayoutAttributeCenterX
439 relatedBy:NSLayoutRelationEqual
440 toItem:parentView
441 attribute:NSLayoutAttributeCenterX
442 multiplier:1
443 constant:0]];
444 }
445
446 void AddSameCenterYConstraint(UIView* parentView,
447 UIView* subview1,
448 UIView* subview2) {
449 DCHECK_EQ(parentView, [subview1 superview]);
450 DCHECK_EQ(parentView, [subview2 superview]);
451 DCHECK_NE(subview1, subview2);
452 [parentView addConstraint:[NSLayoutConstraint
453 constraintWithItem:subview1
454 attribute:NSLayoutAttributeCenterY
455 relatedBy:NSLayoutRelationEqual
456 toItem:subview2
457 attribute:NSLayoutAttributeCenterY
458 multiplier:1
459 constant:0]];
460 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698