WFCCUtilities.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //
  2. // WFCCUtilities.m
  3. // WFChatClient
  4. //
  5. // Created by heavyrain on 2017/9/7.
  6. // Copyright © 2017年 WildFireChat. All rights reserved.
  7. //
  8. #import "WFCCUtilities.h"
  9. #import <CommonCrypto/CommonDigest.h>
  10. #import "WFCCNetworkService.h"
  11. static NSMutableDictionary *wfcUrlImageDict;
  12. static NSLock *wfcImageLock;
  13. @implementation WFCCUtilities
  14. + (CGSize)imageScaleSize:(CGSize)imageSize targetSize:(CGSize)targetSize thumbnailPoint:(CGPoint *)thumbnailPoint {
  15. if (imageSize.width == 0 && imageSize.height == 0) {
  16. return targetSize;
  17. }
  18. CGFloat imageWidth = imageSize.width;
  19. CGFloat imageHeight = imageSize.height;
  20. CGFloat targetWidth = targetSize.width;
  21. CGFloat targetHeight = targetSize.height;
  22. CGFloat scaleFactor = 0.0;
  23. CGFloat scaledWidth = 0.0;
  24. CGFloat scaledHeight = 0.0;
  25. if (imageWidth/imageHeight < 2.4 && imageHeight/imageWidth < 2.4) {
  26. CGFloat widthFactor = targetWidth / imageWidth;
  27. CGFloat heightFactor = targetHeight / imageHeight;
  28. if (widthFactor < heightFactor)
  29. scaleFactor = widthFactor;
  30. else
  31. scaleFactor = heightFactor;
  32. scaledWidth = imageWidth * scaleFactor;
  33. scaledHeight = imageHeight * scaleFactor;
  34. if (widthFactor > heightFactor) {
  35. if (thumbnailPoint) {
  36. thumbnailPoint->y = (targetHeight - scaledHeight) * 0.5;
  37. }
  38. } else if (widthFactor < heightFactor) {
  39. if (thumbnailPoint) {
  40. thumbnailPoint->x = (targetWidth - scaledWidth) * 0.5;
  41. }
  42. }
  43. } else {
  44. if(imageWidth/imageHeight > 2.4) {
  45. scaleFactor = 100 * targetHeight / imageHeight / 240;
  46. } else {
  47. scaleFactor = 100 * targetWidth / imageWidth / 240;
  48. }
  49. scaledWidth = imageWidth * scaleFactor;
  50. scaledHeight = imageHeight * scaleFactor;
  51. }
  52. return CGSizeMake(scaledWidth, scaledHeight);
  53. }
  54. + (UIImage *)generateThumbnail:(UIImage *)image
  55. withWidth:(CGFloat)targetWidth
  56. withHeight:(CGFloat)targetHeight {
  57. UIImage *targetImage = nil;
  58. CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);
  59. CGSize targetSize = [WFCCUtilities imageScaleSize:image.size targetSize:CGSizeMake(targetWidth, targetHeight) thumbnailPoint:&thumbnailPoint];
  60. CGFloat scaledWidth = targetSize.width;
  61. CGFloat scaledHeight = targetSize.height;
  62. CGFloat imageWidth = image.size.width;
  63. CGFloat imageHeight = image.size.height;
  64. UIGraphicsBeginImageContext(CGSizeMake(scaledWidth, scaledHeight));
  65. CGRect thumbnailRect = CGRectZero;
  66. thumbnailRect.origin = thumbnailPoint;
  67. thumbnailRect.size.width = scaledWidth;
  68. thumbnailRect.size.height = scaledHeight;
  69. [image drawInRect:thumbnailRect];
  70. targetImage = UIGraphicsGetImageFromCurrentImageContext();
  71. if(imageWidth/imageHeight > 2.4) {
  72. CGRect rect = CGRectZero;
  73. rect.origin.x = ( targetImage.size.width - 240)/2;
  74. rect.size.width = 240;
  75. rect.origin.y = 0;
  76. rect.size.height = targetImage.size.height;
  77. CGImageRef imageRef = CGImageCreateWithImageInRect([ targetImage CGImage], rect);
  78. targetImage = [UIImage imageWithCGImage:imageRef];
  79. CGImageRelease(imageRef);
  80. } else if(imageHeight/imageWidth > 2.4) {
  81. CGRect rect = CGRectZero;
  82. rect.origin.y = ( targetImage.size.height - 240)/2;
  83. rect.size.height = 240;
  84. rect.origin.x = 0;
  85. rect.size.width = targetImage.size.width;
  86. CGImageRef imageRef = CGImageCreateWithImageInRect([ targetImage CGImage], rect);
  87. targetImage = [UIImage imageWithCGImage:imageRef];
  88. CGImageRelease(imageRef);
  89. }
  90. if ( targetImage == nil)
  91. NSLog(@"could not scale image");
  92. UIGraphicsEndImageContext();
  93. return targetImage;
  94. }
  95. + (NSString *)getSendBoxFilePath:(NSString *)localPath {
  96. if ([[NSFileManager defaultManager] fileExistsAtPath:localPath]) {
  97. return localPath;
  98. } else {
  99. NSUInteger location = [localPath rangeOfString:@"Containers/Data/Application/"].location;
  100. if (location != NSNotFound) {
  101. location =
  102. MIN(MIN([localPath rangeOfString:@"Documents" options:NSCaseInsensitiveSearch range:NSMakeRange(location, localPath.length - location)].location,
  103. [localPath rangeOfString:@"Library" options:NSCaseInsensitiveSearch range:NSMakeRange(location, localPath.length - location)].location),
  104. [localPath rangeOfString:@"tmp" options:NSCaseInsensitiveSearch range:NSMakeRange(location, localPath.length - location)].location);
  105. }
  106. if (location != NSNotFound) {
  107. NSString *relativePath = [localPath substringFromIndex:location];
  108. return [NSHomeDirectory() stringByAppendingPathComponent:relativePath];
  109. } else {
  110. return localPath;
  111. }
  112. }
  113. }
  114. + (NSString *)getDocumentPathWithComponent:(NSString *)componentPath {
  115. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
  116. NSUserDomainMask, YES);
  117. NSString *documentDirectory = [paths objectAtIndex:0];
  118. NSString *path = [documentDirectory stringByAppendingPathComponent:componentPath];
  119. if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
  120. [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
  121. }
  122. return path;
  123. }
  124. + (UIImage *)imageWithRightOrientation:(UIImage *)aImage {
  125. // No-op if the orientation is already correct
  126. if (aImage.imageOrientation == UIImageOrientationUp)
  127. return aImage;
  128. // We need to calculate the proper transformation to make the image upright.
  129. // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
  130. CGAffineTransform transform = CGAffineTransformIdentity;
  131. switch (aImage.imageOrientation) {
  132. case UIImageOrientationDown:
  133. case UIImageOrientationDownMirrored:
  134. transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
  135. transform = CGAffineTransformRotate(transform, M_PI);
  136. break;
  137. case UIImageOrientationLeft:
  138. case UIImageOrientationLeftMirrored:
  139. transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
  140. transform = CGAffineTransformRotate(transform, M_PI_2);
  141. break;
  142. case UIImageOrientationRight:
  143. case UIImageOrientationRightMirrored:
  144. transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
  145. transform = CGAffineTransformRotate(transform, -M_PI_2);
  146. break;
  147. default:
  148. break;
  149. }
  150. switch (aImage.imageOrientation) {
  151. case UIImageOrientationUpMirrored:
  152. case UIImageOrientationDownMirrored:
  153. transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
  154. transform = CGAffineTransformScale(transform, -1, 1);
  155. break;
  156. case UIImageOrientationLeftMirrored:
  157. case UIImageOrientationRightMirrored:
  158. transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
  159. transform = CGAffineTransformScale(transform, -1, 1);
  160. break;
  161. default:
  162. break;
  163. }
  164. // Now we draw the underlying CGImage into a new context, applying the transform
  165. // calculated above.
  166. CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
  167. CGImageGetBitsPerComponent(aImage.CGImage), 0,
  168. CGImageGetColorSpace(aImage.CGImage),
  169. CGImageGetBitmapInfo(aImage.CGImage));
  170. CGContextConcatCTM(ctx, transform);
  171. switch (aImage.imageOrientation) {
  172. case UIImageOrientationLeft:
  173. case UIImageOrientationLeftMirrored:
  174. case UIImageOrientationRight:
  175. case UIImageOrientationRightMirrored:
  176. // Grr...
  177. CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
  178. break;
  179. default:
  180. CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
  181. break;
  182. }
  183. // And now we just create a new UIImage from the drawing context
  184. CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
  185. UIImage *img = [UIImage imageWithCGImage:cgimg];
  186. CGContextRelease(ctx);
  187. CGImageRelease(cgimg);
  188. return img;
  189. }
  190. + (NSString *)rf_EncryptMD5:(NSString *)str {
  191. if (str.length<=0) return nil;
  192. const char *cStr = [str UTF8String];
  193. unsigned char digest[16];
  194. CC_MD5( cStr, (unsigned int)strlen(cStr), digest );
  195. NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
  196. for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
  197. [output appendFormat:@"%02x", digest[i]];
  198. }
  199. return output;
  200. }
  201. + (UIImage *)getUserImage:(NSString *)url {
  202. [wfcImageLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
  203. UIImage *image = [wfcUrlImageDict objectForKey:url];
  204. if (!image) {
  205. image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
  206. if (wfcUrlImageDict.count > 50) {
  207. [wfcUrlImageDict removeAllObjects];
  208. }
  209. [wfcUrlImageDict setObject:image forKey:url];
  210. }
  211. [wfcImageLock unlock];
  212. return image;
  213. }
  214. + (void)generateNewGroupPortrait:(NSString *)groupId
  215. width:(int)PortraitWidth
  216. defaultUserPortrait:(UIImage *(^)(NSString *userId))defaultUserPortraitBlock {
  217. NSNumber *createTime = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"wfc_group_generate_portrait_time_%@", groupId]];
  218. long now = [[[NSDate alloc] init] timeIntervalSince1970];
  219. if ((now - [createTime longLongValue]) < 15) {//防止连续刷新时,多次生成
  220. return;
  221. }
  222. [[NSUserDefaults standardUserDefaults] setObject:@(now) forKey:[NSString stringWithFormat:@"wfc_group_generate_portrait_time_%@", groupId]];
  223. UIView *combineHeadView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, PortraitWidth, PortraitWidth)];
  224. [combineHeadView setBackgroundColor:[UIColor colorWithRed:219.f/255 green:223.f/255 blue:224.f/255 alpha:1.f]];
  225. [[WFCCIMService sharedWFCIMService] getGroupMembers:groupId refresh:NO success:^(NSString *groupId, NSArray<WFCCGroupMember *> *members) {
  226. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  227. if (!members.count) {
  228. return;
  229. }
  230. NSMutableArray *memberIds = [[NSMutableArray alloc] init];
  231. for (WFCCGroupMember *member in members) {
  232. [memberIds addObject:member.memberId];
  233. }
  234. long long now = [[[NSDate alloc] init] timeIntervalSince1970];
  235. int gridMemberCount = MIN((int)memberIds.count, 9);
  236. CGFloat padding = 5;
  237. int numPerRow = 3;
  238. if (gridMemberCount <= 4) {
  239. numPerRow = 2;
  240. }
  241. int row = (int)(gridMemberCount - 1) / numPerRow + 1;
  242. int column = numPerRow;
  243. int firstCol = (int)(gridMemberCount - (row - 1)*column);
  244. CGFloat width = (PortraitWidth - padding) / numPerRow - padding;
  245. CGFloat Y = (PortraitWidth - (row * (width + padding) + padding))/2;
  246. NSString *fullPath = @"";
  247. for (int i = 0; i < row; i++) {
  248. int c = column;
  249. if (i == 0) {
  250. c = firstCol;
  251. }
  252. CGFloat X = (PortraitWidth - (c * (width + padding) + padding))/2;
  253. for (int j = 0; j < c; j++) {
  254. __block UIImageView *imageView;
  255. dispatch_async(dispatch_get_main_queue(), ^{
  256. imageView = [[UIImageView alloc] initWithFrame:CGRectMake(X + j *(width + padding) + padding, Y + i * (width + padding) + padding, width, width)];
  257. });
  258. int index;
  259. if (i == 0) {
  260. index = j;
  261. } else {
  262. index = j + (i-1)*column + firstCol;
  263. }
  264. NSString *userId = [memberIds objectAtIndex:index];
  265. WFCCUserInfo *user = [[WFCCIMService sharedWFCIMService] getUserInfo:userId refresh:NO];
  266. fullPath = [NSString stringWithFormat:@"%@%@", fullPath, user.portrait?user.portrait:userId];
  267. UIImage *image;
  268. if (user.portrait.length) {
  269. image = [WFCCUtilities getUserImage:user.portrait];
  270. if (!image) {
  271. image = defaultUserPortraitBlock(user.userId);
  272. }
  273. } else {
  274. image = defaultUserPortraitBlock(user.userId);
  275. }
  276. dispatch_sync(dispatch_get_main_queue(), ^{
  277. imageView.image = image;
  278. [combineHeadView addSubview:imageView];
  279. });
  280. }
  281. }
  282. __block UIImage *image;
  283. dispatch_sync(dispatch_get_main_queue(), ^{
  284. UIGraphicsBeginImageContextWithOptions(combineHeadView.frame.size, NO, 2.0);
  285. [combineHeadView.layer renderInContext:UIGraphicsGetCurrentContext()];
  286. image = UIGraphicsGetImageFromCurrentImageContext();
  287. UIGraphicsEndImageContext();
  288. });
  289. NSString *mdt = [WFCCUtilities rf_EncryptMD5:fullPath];
  290. NSString *fileName = [NSString stringWithFormat:@"%@-%lld-%d-%@", groupId, now, PortraitWidth, mdt];
  291. NSString *path = [[WFCCUtilities getDocumentPathWithComponent:@"/group_portrait"] stringByAppendingPathComponent:fileName];
  292. NSData *imgData = UIImageJPEGRepresentation(image, 0.85);
  293. [imgData writeToFile:path atomically:YES];
  294. [[NSUserDefaults standardUserDefaults] setObject:path forKey:[NSString stringWithFormat:@"wfc_group_generated_portrait_%@", groupId]];
  295. [[NSUserDefaults standardUserDefaults] synchronize];
  296. dispatch_async(dispatch_get_main_queue(), ^{
  297. [[NSNotificationCenter defaultCenter] postNotificationName:@"GroupPortraitChanged" object:groupId userInfo:@{@"path":path}];
  298. });
  299. });
  300. } error:^(int errorCode) {
  301. }];
  302. }
  303. //如果已经生成了,会立即返回地址。如果没有生成,会返回空,然后生成之后通知GroupPortraitChanged
  304. + (NSString *)getGroupGridPortrait:(NSString *)groupId
  305. width:(int)width
  306. defaultUserPortrait:(UIImage *(^)(NSString *userId))defaultUserPortraitBlock {
  307. //Setp1 检查是否有此群组的记录
  308. NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:@"wfc_group_generated_portrait_%@", groupId]];
  309. if (!path.length) { //记录不存在,生成
  310. [WFCCUtilities generateNewGroupPortrait:groupId width:width defaultUserPortrait:defaultUserPortraitBlock];
  311. } else { //记录存在
  312. //修正沙盒路径
  313. path = [WFCCUtilities getSendBoxFilePath:path];
  314. //检查文件是否存在
  315. NSFileManager* fileManager = [NSFileManager defaultManager];
  316. if ([fileManager fileExistsAtPath:path]) { //文件存在
  317. [[WFCCIMService sharedWFCIMService] getGroupInfo:groupId refresh:NO success:^(WFCCGroupInfo *groupInfo) {
  318. //分析文件名,获取更新时间,hash值
  319. //Path 格式为 groupId-updatetime-width-hash
  320. NSString *str = path.lastPathComponent;
  321. str = [str substringFromIndex:groupId.length];
  322. NSArray *arr = [str componentsSeparatedByString:@"-"];
  323. long long timestamp = [arr[1] longLongValue];
  324. //检查群组日期,超过7天,或生成之后群组有更新,检查头像是否有变化是否需要重新生成
  325. long long now = [[[NSDate alloc] init] timeIntervalSince1970];
  326. if (timestamp + 7 * 24 * 3600 < now || timestamp*1000 < groupInfo.updateTimestamp) {
  327. [[WFCCIMService sharedWFCIMService] getGroupMembers:groupId refresh:NO success:^(NSString *groupId, NSArray<WFCCGroupMember *> *members) {
  328. if (!members.count) {
  329. return;
  330. }
  331. NSString *fullPath = @"";
  332. for (int i = 0; i < MIN(members.count, 9); i++) {
  333. NSString *userId = members[i].memberId;
  334. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:userId refresh:NO];
  335. fullPath = [NSString stringWithFormat:@"%@%@", fullPath, userInfo.portrait ? userInfo.portrait : userId];
  336. }
  337. NSString *mdt = [WFCCUtilities rf_EncryptMD5:fullPath];
  338. if (![mdt isEqualToString:arr[3]]) {
  339. [WFCCUtilities generateNewGroupPortrait:groupId width:width defaultUserPortrait:defaultUserPortraitBlock];
  340. }
  341. } error:^(int errorCode) {
  342. //log here
  343. }];
  344. }
  345. } error:^(int errorCode) {
  346. //log here
  347. }];
  348. return path;
  349. } else { //文件不存在
  350. [WFCCUtilities generateNewGroupPortrait:groupId width:width defaultUserPortrait:defaultUserPortraitBlock];
  351. }
  352. }
  353. return nil;
  354. }
  355. + (void)load {
  356. wfcUrlImageDict = [[NSMutableDictionary alloc] init];
  357. wfcImageLock = [[NSLock alloc] init];
  358. }
  359. @end