VapxAlphaExtractor.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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 "VapxAlphaExtractor.h"
  15. #import <AppKit/AppKit.h>
  16. @implementation VapxAlphaExtractor
  17. + (NSString *)extractWithDir:(NSString *)directory info:(QGVAPCommonInfo**)info completion:(extractCompletionBlock)onCompletion {
  18. VapxAlphaExtractor *extractor = [VapxAlphaExtractor new];
  19. extractor.resourceDirectory = directory;
  20. return [extractor extract:info completion:onCompletion];
  21. }
  22. - (NSString *)extract:(QGVAPCommonInfo**)info completion:(extractCompletionBlock)onCompletion {
  23. if (self.resourceDirectory.length == 0) {
  24. return nil;
  25. }
  26. NSFileManager *fileManager = [NSFileManager defaultManager];
  27. NSString *targetDir = [self.resourceDirectory stringByAppendingPathComponent:@"mergedImages"];
  28. NSString *alphaDir = [self.resourceDirectory stringByAppendingPathComponent:@"alphaImages"];
  29. NSString *rgbDir = [self.resourceDirectory stringByAppendingPathComponent:@"rbgImages"];
  30. if (![fileManager fileExistsAtPath:targetDir]) {
  31. NSError *error = nil;
  32. [fileManager createDirectoryAtPath:targetDir withIntermediateDirectories:YES attributes:nil error:&error];
  33. if (error) {
  34. return nil;
  35. }
  36. }
  37. if (![fileManager fileExistsAtPath:alphaDir]) {
  38. NSError *error = nil;
  39. [fileManager createDirectoryAtPath:alphaDir withIntermediateDirectories:YES attributes:nil error:&error];
  40. if (error) {
  41. return nil;
  42. }
  43. }
  44. if (![fileManager fileExistsAtPath:rgbDir]) {
  45. NSError *error = nil;
  46. [fileManager createDirectoryAtPath:rgbDir withIntermediateDirectories:YES attributes:nil error:&error];
  47. if (error) {
  48. return nil;
  49. }
  50. }
  51. NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.resourceDirectory error:NULL];
  52. __block NSInteger framesCount = 0;
  53. __block NSSize size = NSZeroSize;
  54. NSMutableArray *alphaPaths = [NSMutableArray new];
  55. NSMutableArray *rgbPaths = [NSMutableArray new];
  56. NSArray *sortedArr = [directoryContent sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
  57. return [[obj1 stringByDeletingPathExtension] compare:[obj2 stringByDeletingPathExtension]];
  58. }];
  59. [sortedArr enumerateObjectsUsingBlock:^(NSString *content, NSUInteger idx, BOOL * _Nonnull stop) {
  60. @autoreleasepool {
  61. if (![content.pathExtension isEqualToString:@"png"]) {
  62. NSLog(@"item is not png!:%@", content);
  63. return ;
  64. }
  65. NSImage *image = [[NSImage alloc] initWithContentsOfFile:[self.resourceDirectory stringByAppendingPathComponent:content]];
  66. if (framesCount == 0) {
  67. size = image.size;
  68. } else {
  69. if (!NSEqualSizes(size, image.size)) {
  70. NSLog(@"png size not equal!!:%@ size:%@ first size:%@", content, [NSValue valueWithSize:image.size], [NSValue valueWithSize:size]);
  71. return ;
  72. }
  73. }
  74. [self extractAlphaChannel:image name:content info:info];
  75. [alphaPaths addObject:[alphaDir stringByAppendingPathComponent:content]];
  76. [rgbPaths addObject:[rgbDir stringByAppendingPathComponent:content]];
  77. framesCount += 1;
  78. }
  79. }];
  80. (*info).framesCount = framesCount;
  81. (*info).size = size;
  82. (*info).alphaPaths = alphaPaths;
  83. (*info).rgbPaths = rgbPaths;
  84. if (onCompletion) {
  85. onCompletion(framesCount, alphaPaths, rgbPaths);
  86. }
  87. return targetDir;
  88. }
  89. - (void)saveImage:(NSImage *)image atPath:(NSString *)path {
  90. CGImageRef cgRef = [image CGImageForProposedRect:NULL
  91. context:nil
  92. hints:nil];
  93. NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
  94. [newRep setSize:[image size]]; // if you want the same resolution
  95. NSData *pngData = [newRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
  96. BOOL succ = [pngData writeToFile:path atomically:YES];
  97. if (!succ) {
  98. }
  99. }
  100. - (void)extractAlphaChannel:(NSImage *)image name:(NSString *)name info:(QGVAPCommonInfo**)info {
  101. CGFloat width = image.size.width;
  102. CGFloat height = image.size.height;
  103. NSRect rect = NSMakeRect(0, 0, width, height);
  104. CGImageRef imageRef = [image CGImageForProposedRect:&rect context:nil hints:nil];
  105. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  106. unsigned char *rawData = (unsigned char*)calloc(height * width * 4, sizeof(unsigned char));
  107. unsigned char *alphaData = (unsigned char*)calloc(height * width * 4, sizeof(unsigned char));
  108. unsigned char *rgbData = (unsigned char*)calloc(height * width * 4, sizeof(unsigned char));
  109. NSUInteger bytesPerPixel = 4;
  110. NSUInteger bytesPerRow = bytesPerPixel * width;
  111. NSUInteger bitsPerComponent = 8;
  112. CGContextRef context = CGBitmapContextCreate(rawData, width, height,
  113. bitsPerComponent, bytesPerRow, colorSpace,
  114. kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  115. CGColorSpaceRelease(colorSpace);
  116. CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
  117. CGContextRelease(context);
  118. for (int i = 0; i < height * width * 4; i += 4) {
  119. CGFloat red = ((CGFloat)rawData[i]);
  120. CGFloat green = ((CGFloat)rawData[i+1]);
  121. CGFloat blue = ((CGFloat)rawData[i+2]);
  122. CGFloat alpha = ((CGFloat)rawData[i+3]);
  123. rgbData[i] = red;
  124. rgbData[i+1] = green;
  125. rgbData[i+2] = blue;
  126. rgbData[i+3] = 255;
  127. alphaData[i] = alpha;
  128. alphaData[i+1] = alpha;
  129. alphaData[i+2] = alpha;
  130. alphaData[i+3] = 255;
  131. }
  132. free(rawData);
  133. CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | (CGBitmapInfo)kCGImageAlphaLast;
  134. CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
  135. // 生成 RGB 图片
  136. CGDataProviderRef rgbProvider = CGDataProviderCreateWithData(NULL, rgbData, width*height*4, NULL);
  137. CGImageRef rgbImageRef = CGImageCreate(width, height, 8, 32, 4*width,colorSpace, bitmapInfo, rgbProvider,NULL,NO, renderingIntent);
  138. NSImage *rgbImage = [[NSImage alloc] initWithCGImage:rgbImageRef size: rect.size];
  139. NSString *rgbDir = [self.resourceDirectory stringByAppendingPathComponent:@"rbgImages"];
  140. [self saveImage:rgbImage atPath:[rgbDir stringByAppendingPathComponent:name]];
  141. CGImageRelease(rgbImageRef);
  142. free(rgbData);
  143. CGDataProviderRelease(rgbProvider);
  144. // 生成 Alpha 图片
  145. CGDataProviderRef alphaProvider = CGDataProviderCreateWithData(NULL, alphaData, width*height*4, NULL);
  146. CGImageRef alphaImageRef = CGImageCreate(width, height, 8, 32, 4*width,colorSpace, bitmapInfo, alphaProvider,NULL,NO, renderingIntent);
  147. NSImage *alphaImage = [[NSImage alloc] initWithCGImage:alphaImageRef size:rect.size];
  148. NSString *alphaDir = [self.resourceDirectory stringByAppendingPathComponent:@"alphaImages"];
  149. [self saveImage:alphaImage atPath:[alphaDir stringByAppendingPathComponent:name]];
  150. CGImageRelease(alphaImageRef);
  151. free(alphaData);
  152. CGDataProviderRelease(alphaProvider);
  153. }
  154. - (NSRect)alphaRectForPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  155. NSSize totalSize = [self mergedSizeWithPosition:position rgbSize:size alphaScale:scale];
  156. NSSize alphaSize = NSMakeSize(size.width*scale, size.height*scale);
  157. return [self rectForSize:alphaSize containerSize:totalSize position:position];
  158. }
  159. - (NSRect)rgbRectForPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  160. NSSize totalSize = [self mergedSizeWithPosition:position rgbSize:size alphaScale:scale];
  161. VapxAlphaPostion rgbPostion = VapxAlphaPostion_rightCenter;
  162. switch (position) {
  163. case VapxAlphaPostion_leftTop:
  164. case VapxAlphaPostion_leftCenter:
  165. case VapxAlphaPostion_leftBottom:
  166. rgbPostion = VapxAlphaPostion_rightTop;
  167. break;
  168. case VapxAlphaPostion_rightTop:
  169. case VapxAlphaPostion_rightCenter:
  170. case VapxAlphaPostion_rightBottom:
  171. rgbPostion = VapxAlphaPostion_leftTop;
  172. break;
  173. case VapxAlphaPostion_bottomLeft:
  174. case VapxAlphaPostion_bottomRight:
  175. case VapxAlphaPostion_bottomCenter:
  176. rgbPostion = VapxAlphaPostion_topLeft;
  177. break;
  178. case VapxAlphaPostion_topLeft:
  179. case VapxAlphaPostion_topRight:
  180. case VapxAlphaPostion_topCenter:
  181. rgbPostion = VapxAlphaPostion_bottomLeft;
  182. break;
  183. default:
  184. break;
  185. }
  186. return [self rectForSize:size containerSize:totalSize position:rgbPostion];
  187. }
  188. - (NSRect)rectForSize:(NSSize)size containerSize:(NSSize)containerSize position:(VapxAlphaPostion)position {
  189. CGFloat x,y;
  190. switch (position) {
  191. case VapxAlphaPostion_leftTop:
  192. x = 0;
  193. y = containerSize.height-size.height;
  194. break;
  195. case VapxAlphaPostion_leftCenter:
  196. x = 0;
  197. y = (containerSize.height-size.height)/2.0;
  198. break;
  199. case VapxAlphaPostion_leftBottom:
  200. x = 0;
  201. y = 0;
  202. break;
  203. case VapxAlphaPostion_rightTop:
  204. x = containerSize.width - size.width;
  205. y = containerSize.height-size.height;
  206. break;
  207. case VapxAlphaPostion_rightCenter:
  208. x = containerSize.width - size.width;
  209. y = (containerSize.height-size.height)/2.0;
  210. break;
  211. case VapxAlphaPostion_rightBottom:
  212. x = containerSize.width - size.width;
  213. y = 0;
  214. break;
  215. case VapxAlphaPostion_bottomLeft:
  216. x = 0;
  217. y = 0;
  218. break;
  219. case VapxAlphaPostion_bottomRight:
  220. x = containerSize.width - size.width;
  221. y = 0;
  222. break;
  223. case VapxAlphaPostion_bottomCenter:
  224. x = (containerSize.width-size.width)/2.0;
  225. y = 0;
  226. break;
  227. case VapxAlphaPostion_topLeft:
  228. x = 0;
  229. y = containerSize.height-size.height;
  230. break;
  231. case VapxAlphaPostion_topRight:
  232. x = containerSize.width - size.width;
  233. y = containerSize.height-size.height;
  234. break;
  235. case VapxAlphaPostion_topCenter:
  236. x = (containerSize.width-size.width)/2.0;
  237. y = containerSize.height-size.height;
  238. break;
  239. default:
  240. break;
  241. }
  242. return NSMakeRect(x, y, size.width, size.height);
  243. }
  244. - (NSSize)mergedSizeWithPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  245. switch (position) {
  246. case VapxAlphaPostion_leftTop:
  247. case VapxAlphaPostion_leftCenter:
  248. case VapxAlphaPostion_leftBottom:
  249. case VapxAlphaPostion_rightTop:
  250. case VapxAlphaPostion_rightCenter:
  251. case VapxAlphaPostion_rightBottom:
  252. return NSMakeSize(size.width+size.width*scale, size.height);
  253. case VapxAlphaPostion_bottomLeft:
  254. case VapxAlphaPostion_bottomRight:
  255. case VapxAlphaPostion_bottomCenter:
  256. case VapxAlphaPostion_topLeft:
  257. case VapxAlphaPostion_topRight:
  258. case VapxAlphaPostion_topCenter:
  259. return NSMakeSize(size.width, size.height+size.height*scale);
  260. default:
  261. break;
  262. }
  263. }
  264. @end