// // YYCGUtilities.h // YYKit // // Created by ibireme on 15/2/28. // Copyright (c) 2015 ibireme. // // This source code is licensed under the MIT-style license found in the // LICENSE file in the root directory of this source tree. // #import #import #if __has_include() #import #else #import "YYKitMacro.h" #endif YY_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN /// Create an `ARGB` Bitmap context. Returns NULL if an error occurs. /// /// @discussion The function is same as UIGraphicsBeginImageContextWithOptions(), /// but it doesn't push the context to UIGraphic, so you can retain the context for reuse. CGContextRef _Nullable YYCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque, CGFloat scale); /// Create a `DeviceGray` Bitmap context. Returns NULL if an error occurs. CGContextRef _Nullable YYCGContextCreateGrayBitmapContext(CGSize size, CGFloat scale); /// Get main screen's scale. CGFloat YYScreenScale(); /// Get main screen's size. Height is always larger than width. CGSize YYScreenSize(); /// Convert degrees to radians. static inline CGFloat DegreesToRadians(CGFloat degrees) { return degrees * M_PI / 180; } /// Convert radians to degrees. static inline CGFloat RadiansToDegrees(CGFloat radians) { return radians * 180 / M_PI; } /// Get the transform rotation. /// @return the rotation in radians [-PI,PI] ([-180°,180°]) static inline CGFloat CGAffineTransformGetRotation(CGAffineTransform transform) { return atan2(transform.b, transform.a); } /// Get the transform's scale.x static inline CGFloat CGAffineTransformGetScaleX(CGAffineTransform transform) { return sqrt(transform.a * transform.a + transform.c * transform.c); } /// Get the transform's scale.y static inline CGFloat CGAffineTransformGetScaleY(CGAffineTransform transform) { return sqrt(transform.b * transform.b + transform.d * transform.d); } /// Get the transform's translate.x static inline CGFloat CGAffineTransformGetTranslateX(CGAffineTransform transform) { return transform.tx; } /// Get the transform's translate.y static inline CGFloat CGAffineTransformGetTranslateY(CGAffineTransform transform) { return transform.ty; } /** If you have 3 pair of points transformed by a same CGAffineTransform: p1 (transform->) q1 p2 (transform->) q2 p3 (transform->) q3 This method returns the original transform matrix from these 3 pair of points. @see http://stackoverflow.com/questions/13291796/calculate-values-for-a-cgaffinetransform-from-three-points-in-each-of-two-uiview */ CGAffineTransform YYCGAffineTransformGetFromPoints(CGPoint before[_Nonnull 3], CGPoint after[_Nonnull 3]); /// Get the transform which can converts a point from the coordinate system of a given view to another. CGAffineTransform YYCGAffineTransformGetFromViews(UIView *from, UIView *to); /// Create a skew transform. static inline CGAffineTransform CGAffineTransformMakeSkew(CGFloat x, CGFloat y){ CGAffineTransform transform = CGAffineTransformIdentity; transform.c = -x; transform.b = y; return transform; } /// Negates/inverts a UIEdgeInsets. static inline UIEdgeInsets UIEdgeInsetsInvert(UIEdgeInsets insets) { return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right); } /// Convert CALayer's gravity string to UIViewContentMode. UIViewContentMode YYCAGravityToUIViewContentMode(NSString *gravity); /// Convert UIViewContentMode to CALayer's gravity string. NSString *YYUIViewContentModeToCAGravity(UIViewContentMode contentMode); /** Returns a rectangle to fit the `rect` with specified content mode. @param rect The constrant rect @param size The content size @param mode The content mode @return A rectangle for the given content mode. @discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill. */ CGRect YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode); /// Returns the center for the rectangle. static inline CGPoint CGRectGetCenter(CGRect rect) { return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); } /// Returns the area of the rectangle. static inline CGFloat CGRectGetArea(CGRect rect) { if (CGRectIsNull(rect)) return 0; rect = CGRectStandardize(rect); return rect.size.width * rect.size.height; } /// Returns the distance between two points. static inline CGFloat CGPointGetDistanceToPoint(CGPoint p1, CGPoint p2) { return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } /// Returns the minmium distance between a point to a rectangle. static inline CGFloat CGPointGetDistanceToRect(CGPoint p, CGRect r) { r = CGRectStandardize(r); if (CGRectContainsPoint(r, p)) return 0; CGFloat distV, distH; if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) { distV = 0; } else { distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r); } if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) { distH = 0; } else { distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r); } return MAX(distV, distH); } /// Convert point to pixel. static inline CGFloat CGFloatToPixel(CGFloat value) { return value * YYScreenScale(); } /// Convert pixel to point. static inline CGFloat CGFloatFromPixel(CGFloat value) { return value / YYScreenScale(); } /// floor point value for pixel-aligned static inline CGFloat CGFloatPixelFloor(CGFloat value) { CGFloat scale = YYScreenScale(); return floor(value * scale) / scale; } /// round point value for pixel-aligned static inline CGFloat CGFloatPixelRound(CGFloat value) { CGFloat scale = YYScreenScale(); return round(value * scale) / scale; } /// ceil point value for pixel-aligned static inline CGFloat CGFloatPixelCeil(CGFloat value) { CGFloat scale = YYScreenScale(); return ceil(value * scale) / scale; } /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) static inline CGFloat CGFloatPixelHalf(CGFloat value) { CGFloat scale = YYScreenScale(); return (floor(value * scale) + 0.5) / scale; } /// floor point value for pixel-aligned static inline CGPoint CGPointPixelFloor(CGPoint point) { CGFloat scale = YYScreenScale(); return CGPointMake(floor(point.x * scale) / scale, floor(point.y * scale) / scale); } /// round point value for pixel-aligned static inline CGPoint CGPointPixelRound(CGPoint point) { CGFloat scale = YYScreenScale(); return CGPointMake(round(point.x * scale) / scale, round(point.y * scale) / scale); } /// ceil point value for pixel-aligned static inline CGPoint CGPointPixelCeil(CGPoint point) { CGFloat scale = YYScreenScale(); return CGPointMake(ceil(point.x * scale) / scale, ceil(point.y * scale) / scale); } /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) static inline CGPoint CGPointPixelHalf(CGPoint point) { CGFloat scale = YYScreenScale(); return CGPointMake((floor(point.x * scale) + 0.5) / scale, (floor(point.y * scale) + 0.5) / scale); } /// floor point value for pixel-aligned static inline CGSize CGSizePixelFloor(CGSize size) { CGFloat scale = YYScreenScale(); return CGSizeMake(floor(size.width * scale) / scale, floor(size.height * scale) / scale); } /// round point value for pixel-aligned static inline CGSize CGSizePixelRound(CGSize size) { CGFloat scale = YYScreenScale(); return CGSizeMake(round(size.width * scale) / scale, round(size.height * scale) / scale); } /// ceil point value for pixel-aligned static inline CGSize CGSizePixelCeil(CGSize size) { CGFloat scale = YYScreenScale(); return CGSizeMake(ceil(size.width * scale) / scale, ceil(size.height * scale) / scale); } /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) static inline CGSize CGSizePixelHalf(CGSize size) { CGFloat scale = YYScreenScale(); return CGSizeMake((floor(size.width * scale) + 0.5) / scale, (floor(size.height * scale) + 0.5) / scale); } /// floor point value for pixel-aligned static inline CGRect CGRectPixelFloor(CGRect rect) { CGPoint origin = CGPointPixelCeil(rect.origin); CGPoint corner = CGPointPixelFloor(CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)); CGRect ret = CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); if (ret.size.width < 0) ret.size.width = 0; if (ret.size.height < 0) ret.size.height = 0; return ret; } /// round point value for pixel-aligned static inline CGRect CGRectPixelRound(CGRect rect) { CGPoint origin = CGPointPixelRound(rect.origin); CGPoint corner = CGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)); return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); } /// ceil point value for pixel-aligned static inline CGRect CGRectPixelCeil(CGRect rect) { CGPoint origin = CGPointPixelFloor(rect.origin); CGPoint corner = CGPointPixelCeil(CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)); return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); } /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) static inline CGRect CGRectPixelHalf(CGRect rect) { CGPoint origin = CGPointPixelHalf(rect.origin); CGPoint corner = CGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)); return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); } /// floor UIEdgeInset for pixel-aligned static inline UIEdgeInsets UIEdgeInsetPixelFloor(UIEdgeInsets insets) { insets.top = CGFloatPixelFloor(insets.top); insets.left = CGFloatPixelFloor(insets.left); insets.bottom = CGFloatPixelFloor(insets.bottom); insets.right = CGFloatPixelFloor(insets.right); return insets; } /// ceil UIEdgeInset for pixel-aligned static inline UIEdgeInsets UIEdgeInsetPixelCeil(UIEdgeInsets insets) { insets.top = CGFloatPixelCeil(insets.top); insets.left = CGFloatPixelCeil(insets.left); insets.bottom = CGFloatPixelCeil(insets.bottom); insets.right = CGFloatPixelCeil(insets.right); return insets; } // main screen's scale #ifndef kScreenScale #define kScreenScale YYScreenScale() #endif // main screen's size (portrait) #ifndef kScreenSize #define kScreenSize YYScreenSize() #endif // main screen's width (portrait) #ifndef kWidth #define kWidth YYScreenSize().width #endif // main screen's height (portrait) #ifndef kHeight #define kHeight YYScreenSize().height #endif NS_ASSUME_NONNULL_END YY_EXTERN_C_END