VapxMaskInfoGenerator.m 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Tencent is pleased to support the open source community by making vap available.
  2. //
  3. // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  4. //
  5. // Licensed under the MIT License (the "License"); you may not use this file except in
  6. // compliance with the License. You may obtain a copy of the License at
  7. //
  8. // http://opensource.org/licenses/MIT
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed under the License is
  11. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  12. // either express or implied. See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "VapxMaskInfoGenerator.h"
  15. @implementation VapxMaskInfoGenerator
  16. + (NSDictionary<NSNumber *,NSArray<QGVAPMergedInfo *> *> *)mergeInfoAt:(NSArray<NSString *> *)paths sources:(NSArray<QGVAPSourceInfo *> *)sources frames:(NSInteger)frameCount {
  17. if (paths.count != sources.count) {
  18. NSLog(@"遮罩与资源信息数量不匹配!");
  19. return nil;
  20. }
  21. if (frameCount <= 0) {
  22. NSLog(@"帧数不对");
  23. return nil;
  24. }
  25. //每个遮罩分别解析
  26. NSMutableArray<NSDictionary <NSNumber *, QGVAPMergedInfo *> *> *mergeInfoArr = [NSMutableArray new];
  27. NSInteger count = paths.count;
  28. for (int i = 0; i < count; i ++) {
  29. @autoreleasepool {
  30. NSString *path = paths[i];
  31. QGVAPSourceInfo *source = sources[i];
  32. NSDictionary <NSNumber *, QGVAPMergedInfo *> *mergeInfo = [self mergeInfoAt:path sourceID:source index:i];
  33. [mergeInfoArr addObject:mergeInfo];
  34. }
  35. }
  36. //遮罩信息根据帧index合并
  37. NSMutableDictionary<NSNumber *,NSArray<QGVAPMergedInfo *> *> *totalMergeInfo = [NSMutableDictionary new];
  38. for (int i = 0; i < frameCount; i ++) {
  39. @autoreleasepool {
  40. NSMutableArray<QGVAPMergedInfo *> *mergeInfosForCurrentFrame = [NSMutableArray new];
  41. [mergeInfoArr enumerateObjectsUsingBlock:^(NSDictionary<NSNumber *,QGVAPMergedInfo *> * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  42. QGVAPMergedInfo *mergeInfo = obj[@(i)];
  43. if (mergeInfo) {
  44. [mergeInfosForCurrentFrame addObject:mergeInfo];
  45. }
  46. }];
  47. if (mergeInfosForCurrentFrame.count > 0) {
  48. totalMergeInfo[@(i)] = mergeInfosForCurrentFrame;
  49. }
  50. }
  51. }
  52. return totalMergeInfo;
  53. }
  54. + (NSDictionary <NSNumber *, QGVAPMergedInfo *> *)mergeInfoAt:(NSString *)path sourceID:(QGVAPSourceInfo *)source index:(NSInteger)index {
  55. NSMutableDictionary <NSNumber *, QGVAPMergedInfo *>* mergeInfoDic = [NSMutableDictionary new];
  56. NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
  57. NSFileManager *fileManager = [NSFileManager defaultManager];
  58. NSString *targetDir = [path stringByAppendingPathComponent:@"maskOutPut"];
  59. if (![fileManager fileExistsAtPath:targetDir]) {
  60. NSError *error = nil;
  61. [fileManager createDirectoryAtPath:targetDir withIntermediateDirectories:YES attributes:nil error:&error];
  62. if (error) {
  63. NSLog(@"error create dir fail:%@", targetDir);
  64. return nil;
  65. }
  66. }
  67. [directoryContent enumerateObjectsUsingBlock:^(NSString *content, NSUInteger idx, BOOL * _Nonnull stop) {
  68. @autoreleasepool {
  69. if (![content.pathExtension isEqualToString:@"png"]) {
  70. NSLog(@"item is not png!:%@", content);
  71. return ;
  72. }
  73. NSImage *image = [[NSImage alloc] initWithContentsOfFile:[path stringByAppendingPathComponent:content]];
  74. QGVAPMergedInfo *mergeInfo = [self mergeInfoFor:image desPath:[targetDir stringByAppendingPathComponent:content] sourceInfo:source];
  75. if (!mergeInfo) {
  76. NSLog(@"mask has no valid area:%@", [targetDir stringByAppendingPathComponent:content]);
  77. return ;
  78. }
  79. mergeInfo.renderIndex = index;
  80. mergeInfo.source = source;
  81. NSInteger frameIndex = [[content stringByDeletingPathExtension] integerValue];
  82. mergeInfoDic[@(frameIndex)] = mergeInfo;
  83. }
  84. }];
  85. return mergeInfoDic;
  86. }
  87. + (QGVAPMergedInfo *)mergeInfoFor:(NSImage *)image desPath:(NSString *)path sourceInfo:(QGVAPSourceInfo *)source {
  88. QGVAPMergedInfo *mergeInfo = [QGVAPMergedInfo new];
  89. CGFloat width = image.size.width;
  90. CGFloat height = image.size.height;
  91. NSRect rect = NSMakeRect(0, 0, width, height);
  92. CGImageRef imageRef = [image CGImageForProposedRect:&rect context:nil hints:nil];
  93. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  94. unsigned char *rawData = (unsigned char*)calloc(height * width * 4, sizeof(unsigned char));
  95. NSUInteger bytesPerPixel = 4;
  96. NSUInteger bytesPerRow = bytesPerPixel * width;
  97. NSUInteger bitsPerComponent = 8;
  98. CGContextRef context = CGBitmapContextCreate(rawData, width, height,
  99. bitsPerComponent, bytesPerRow, colorSpace,
  100. kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  101. CGColorSpaceRelease(colorSpace);
  102. CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
  103. CGContextRelease(context);
  104. CGFloat minX = -1, minY = -1, maxX = -1, maxY = -1;
  105. for (int i = 0; i < height * width * 4; i += 4) {
  106. CGFloat alpha = ((CGFloat)rawData[i+3]);
  107. if (alpha == 0) {
  108. continue ;
  109. }
  110. NSInteger x = (i/4)%((int)width);
  111. NSInteger y = (i/4)/((int)width);
  112. if (minX == -1) {
  113. minX = x;
  114. minY = y;
  115. maxX = x;
  116. maxY = y;
  117. } else {
  118. minX = MIN(minX, x);
  119. minY = MIN(minY, y);
  120. maxX = MAX(maxX, x);
  121. maxY = MAX(maxY, y);
  122. }
  123. }
  124. if (minX >= maxX || minY >= maxY) {
  125. //无有效区域
  126. free(rawData);
  127. return nil;
  128. }
  129. maxX += 1;
  130. maxY += 1;
  131. NSRect maskRect = NSMakeRect(minX, minY, maxX-minX, maxY-minY);
  132. mergeInfo.renderRect = maskRect;
  133. unsigned char *maskData = (unsigned char*)calloc(maskRect.size.height * maskRect.size.width * 4, sizeof(unsigned char));
  134. NSInteger maskDataIndex = 0;
  135. for (int i = minY; i < maxY; i ++) {
  136. for (int j = minX; j < maxX; j ++) {
  137. NSInteger index = i*width*4 + j*4;
  138. CGFloat v = 255;
  139. CGFloat r = rawData[index];
  140. CGFloat a = rawData[index+3];
  141. if (a == 0) {
  142. v = 0;
  143. } else {
  144. v = (255-r)*a/255.0;
  145. }
  146. maskData[maskDataIndex++] = v;
  147. maskData[maskDataIndex++] = v;
  148. maskData[maskDataIndex++] = v;
  149. maskData[maskDataIndex++] = 255;
  150. }
  151. }
  152. CGDataProviderRef maskProvider = CGDataProviderCreateWithData(NULL, maskData, maskRect.size.width*maskRect.size.height*4, NULL);
  153. CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | (CGBitmapInfo)kCGImageAlphaLast;
  154. CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
  155. CGImageRef maskImageRef = CGImageCreate(maskRect.size.width, maskRect.size.height, 8, 32, 4*maskRect.size.width,colorSpace, bitmapInfo, maskProvider,NULL,NO, renderingIntent);
  156. CGDataProviderRelease(maskProvider);
  157. free(maskData);
  158. free(rawData);
  159. NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithCGImage:maskImageRef];
  160. CGImageRelease(maskImageRef);
  161. [newRep setSize:maskRect.size]; // if you want the same resolution
  162. NSData *pngData = [newRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
  163. BOOL succ = [pngData writeToFile:path atomically:YES];
  164. if (!succ) {
  165. NSLog(@"save png fail!:%@ image:%@" , path, image);
  166. }
  167. mergeInfo.tempPathForMask = path;
  168. return mergeInfo;
  169. }
  170. @end