YYCGUtilities.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. //
  2. // YYCGUtilities.h
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/2/28.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import <UIKit/UIKit.h>
  12. #import <QuartzCore/QuartzCore.h>
  13. #if __has_include(<YYKit/YYKit.h>)
  14. #import <YYKit/YYKitMacro.h>
  15. #else
  16. #import "YYKitMacro.h"
  17. #endif
  18. YY_EXTERN_C_BEGIN
  19. NS_ASSUME_NONNULL_BEGIN
  20. /// Create an `ARGB` Bitmap context. Returns NULL if an error occurs.
  21. ///
  22. /// @discussion The function is same as UIGraphicsBeginImageContextWithOptions(),
  23. /// but it doesn't push the context to UIGraphic, so you can retain the context for reuse.
  24. CGContextRef _Nullable YYCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque, CGFloat scale);
  25. /// Create a `DeviceGray` Bitmap context. Returns NULL if an error occurs.
  26. CGContextRef _Nullable YYCGContextCreateGrayBitmapContext(CGSize size, CGFloat scale);
  27. /// Get main screen's scale.
  28. CGFloat YYScreenScale();
  29. /// Get main screen's size. Height is always larger than width.
  30. CGSize YYScreenSize();
  31. /// Convert degrees to radians.
  32. static inline CGFloat DegreesToRadians(CGFloat degrees) {
  33. return degrees * M_PI / 180;
  34. }
  35. /// Convert radians to degrees.
  36. static inline CGFloat RadiansToDegrees(CGFloat radians) {
  37. return radians * 180 / M_PI;
  38. }
  39. /// Get the transform rotation.
  40. /// @return the rotation in radians [-PI,PI] ([-180°,180°])
  41. static inline CGFloat CGAffineTransformGetRotation(CGAffineTransform transform) {
  42. return atan2(transform.b, transform.a);
  43. }
  44. /// Get the transform's scale.x
  45. static inline CGFloat CGAffineTransformGetScaleX(CGAffineTransform transform) {
  46. return sqrt(transform.a * transform.a + transform.c * transform.c);
  47. }
  48. /// Get the transform's scale.y
  49. static inline CGFloat CGAffineTransformGetScaleY(CGAffineTransform transform) {
  50. return sqrt(transform.b * transform.b + transform.d * transform.d);
  51. }
  52. /// Get the transform's translate.x
  53. static inline CGFloat CGAffineTransformGetTranslateX(CGAffineTransform transform) {
  54. return transform.tx;
  55. }
  56. /// Get the transform's translate.y
  57. static inline CGFloat CGAffineTransformGetTranslateY(CGAffineTransform transform) {
  58. return transform.ty;
  59. }
  60. /**
  61. If you have 3 pair of points transformed by a same CGAffineTransform:
  62. p1 (transform->) q1
  63. p2 (transform->) q2
  64. p3 (transform->) q3
  65. This method returns the original transform matrix from these 3 pair of points.
  66. @see http://stackoverflow.com/questions/13291796/calculate-values-for-a-cgaffinetransform-from-three-points-in-each-of-two-uiview
  67. */
  68. CGAffineTransform YYCGAffineTransformGetFromPoints(CGPoint before[_Nonnull 3], CGPoint after[_Nonnull 3]);
  69. /// Get the transform which can converts a point from the coordinate system of a given view to another.
  70. CGAffineTransform YYCGAffineTransformGetFromViews(UIView *from, UIView *to);
  71. /// Create a skew transform.
  72. static inline CGAffineTransform CGAffineTransformMakeSkew(CGFloat x, CGFloat y){
  73. CGAffineTransform transform = CGAffineTransformIdentity;
  74. transform.c = -x;
  75. transform.b = y;
  76. return transform;
  77. }
  78. /// Negates/inverts a UIEdgeInsets.
  79. static inline UIEdgeInsets UIEdgeInsetsInvert(UIEdgeInsets insets) {
  80. return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right);
  81. }
  82. /// Convert CALayer's gravity string to UIViewContentMode.
  83. UIViewContentMode YYCAGravityToUIViewContentMode(NSString *gravity);
  84. /// Convert UIViewContentMode to CALayer's gravity string.
  85. NSString *YYUIViewContentModeToCAGravity(UIViewContentMode contentMode);
  86. /**
  87. Returns a rectangle to fit the `rect` with specified content mode.
  88. @param rect The constrant rect
  89. @param size The content size
  90. @param mode The content mode
  91. @return A rectangle for the given content mode.
  92. @discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill.
  93. */
  94. CGRect YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode);
  95. /// Returns the center for the rectangle.
  96. static inline CGPoint CGRectGetCenter(CGRect rect) {
  97. return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
  98. }
  99. /// Returns the area of the rectangle.
  100. static inline CGFloat CGRectGetArea(CGRect rect) {
  101. if (CGRectIsNull(rect)) return 0;
  102. rect = CGRectStandardize(rect);
  103. return rect.size.width * rect.size.height;
  104. }
  105. /// Returns the distance between two points.
  106. static inline CGFloat CGPointGetDistanceToPoint(CGPoint p1, CGPoint p2) {
  107. return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
  108. }
  109. /// Returns the minmium distance between a point to a rectangle.
  110. static inline CGFloat CGPointGetDistanceToRect(CGPoint p, CGRect r) {
  111. r = CGRectStandardize(r);
  112. if (CGRectContainsPoint(r, p)) return 0;
  113. CGFloat distV, distH;
  114. if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) {
  115. distV = 0;
  116. } else {
  117. distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r);
  118. }
  119. if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) {
  120. distH = 0;
  121. } else {
  122. distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r);
  123. }
  124. return MAX(distV, distH);
  125. }
  126. /// Convert point to pixel.
  127. static inline CGFloat CGFloatToPixel(CGFloat value) {
  128. return value * YYScreenScale();
  129. }
  130. /// Convert pixel to point.
  131. static inline CGFloat CGFloatFromPixel(CGFloat value) {
  132. return value / YYScreenScale();
  133. }
  134. /// floor point value for pixel-aligned
  135. static inline CGFloat CGFloatPixelFloor(CGFloat value) {
  136. CGFloat scale = YYScreenScale();
  137. return floor(value * scale) / scale;
  138. }
  139. /// round point value for pixel-aligned
  140. static inline CGFloat CGFloatPixelRound(CGFloat value) {
  141. CGFloat scale = YYScreenScale();
  142. return round(value * scale) / scale;
  143. }
  144. /// ceil point value for pixel-aligned
  145. static inline CGFloat CGFloatPixelCeil(CGFloat value) {
  146. CGFloat scale = YYScreenScale();
  147. return ceil(value * scale) / scale;
  148. }
  149. /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
  150. static inline CGFloat CGFloatPixelHalf(CGFloat value) {
  151. CGFloat scale = YYScreenScale();
  152. return (floor(value * scale) + 0.5) / scale;
  153. }
  154. /// floor point value for pixel-aligned
  155. static inline CGPoint CGPointPixelFloor(CGPoint point) {
  156. CGFloat scale = YYScreenScale();
  157. return CGPointMake(floor(point.x * scale) / scale,
  158. floor(point.y * scale) / scale);
  159. }
  160. /// round point value for pixel-aligned
  161. static inline CGPoint CGPointPixelRound(CGPoint point) {
  162. CGFloat scale = YYScreenScale();
  163. return CGPointMake(round(point.x * scale) / scale,
  164. round(point.y * scale) / scale);
  165. }
  166. /// ceil point value for pixel-aligned
  167. static inline CGPoint CGPointPixelCeil(CGPoint point) {
  168. CGFloat scale = YYScreenScale();
  169. return CGPointMake(ceil(point.x * scale) / scale,
  170. ceil(point.y * scale) / scale);
  171. }
  172. /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
  173. static inline CGPoint CGPointPixelHalf(CGPoint point) {
  174. CGFloat scale = YYScreenScale();
  175. return CGPointMake((floor(point.x * scale) + 0.5) / scale,
  176. (floor(point.y * scale) + 0.5) / scale);
  177. }
  178. /// floor point value for pixel-aligned
  179. static inline CGSize CGSizePixelFloor(CGSize size) {
  180. CGFloat scale = YYScreenScale();
  181. return CGSizeMake(floor(size.width * scale) / scale,
  182. floor(size.height * scale) / scale);
  183. }
  184. /// round point value for pixel-aligned
  185. static inline CGSize CGSizePixelRound(CGSize size) {
  186. CGFloat scale = YYScreenScale();
  187. return CGSizeMake(round(size.width * scale) / scale,
  188. round(size.height * scale) / scale);
  189. }
  190. /// ceil point value for pixel-aligned
  191. static inline CGSize CGSizePixelCeil(CGSize size) {
  192. CGFloat scale = YYScreenScale();
  193. return CGSizeMake(ceil(size.width * scale) / scale,
  194. ceil(size.height * scale) / scale);
  195. }
  196. /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
  197. static inline CGSize CGSizePixelHalf(CGSize size) {
  198. CGFloat scale = YYScreenScale();
  199. return CGSizeMake((floor(size.width * scale) + 0.5) / scale,
  200. (floor(size.height * scale) + 0.5) / scale);
  201. }
  202. /// floor point value for pixel-aligned
  203. static inline CGRect CGRectPixelFloor(CGRect rect) {
  204. CGPoint origin = CGPointPixelCeil(rect.origin);
  205. CGPoint corner = CGPointPixelFloor(CGPointMake(rect.origin.x + rect.size.width,
  206. rect.origin.y + rect.size.height));
  207. CGRect ret = CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
  208. if (ret.size.width < 0) ret.size.width = 0;
  209. if (ret.size.height < 0) ret.size.height = 0;
  210. return ret;
  211. }
  212. /// round point value for pixel-aligned
  213. static inline CGRect CGRectPixelRound(CGRect rect) {
  214. CGPoint origin = CGPointPixelRound(rect.origin);
  215. CGPoint corner = CGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width,
  216. rect.origin.y + rect.size.height));
  217. return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
  218. }
  219. /// ceil point value for pixel-aligned
  220. static inline CGRect CGRectPixelCeil(CGRect rect) {
  221. CGPoint origin = CGPointPixelFloor(rect.origin);
  222. CGPoint corner = CGPointPixelCeil(CGPointMake(rect.origin.x + rect.size.width,
  223. rect.origin.y + rect.size.height));
  224. return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
  225. }
  226. /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
  227. static inline CGRect CGRectPixelHalf(CGRect rect) {
  228. CGPoint origin = CGPointPixelHalf(rect.origin);
  229. CGPoint corner = CGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width,
  230. rect.origin.y + rect.size.height));
  231. return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
  232. }
  233. /// floor UIEdgeInset for pixel-aligned
  234. static inline UIEdgeInsets UIEdgeInsetPixelFloor(UIEdgeInsets insets) {
  235. insets.top = CGFloatPixelFloor(insets.top);
  236. insets.left = CGFloatPixelFloor(insets.left);
  237. insets.bottom = CGFloatPixelFloor(insets.bottom);
  238. insets.right = CGFloatPixelFloor(insets.right);
  239. return insets;
  240. }
  241. /// ceil UIEdgeInset for pixel-aligned
  242. static inline UIEdgeInsets UIEdgeInsetPixelCeil(UIEdgeInsets insets) {
  243. insets.top = CGFloatPixelCeil(insets.top);
  244. insets.left = CGFloatPixelCeil(insets.left);
  245. insets.bottom = CGFloatPixelCeil(insets.bottom);
  246. insets.right = CGFloatPixelCeil(insets.right);
  247. return insets;
  248. }
  249. // main screen's scale
  250. #ifndef kScreenScale
  251. #define kScreenScale YYScreenScale()
  252. #endif
  253. // main screen's size (portrait)
  254. #ifndef kScreenSize
  255. #define kScreenSize YYScreenSize()
  256. #endif
  257. // main screen's width (portrait)
  258. #ifndef kWidth
  259. #define kWidth YYScreenSize().width
  260. #endif
  261. // main screen's height (portrait)
  262. #ifndef kHeight
  263. #define kHeight YYScreenSize().height
  264. #endif
  265. NS_ASSUME_NONNULL_END
  266. YY_EXTERN_C_END