VapxAlphaExtractor.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. CGDataProviderRef rgbProvider = CGDataProviderCreateWithData(NULL, rgbData, width*height*4, NULL);
  134. CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | (CGBitmapInfo)kCGImageAlphaLast;
  135. CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
  136. CGImageRef rgbImageRef = CGImageCreate(width, height, 8, 32, 4*width,colorSpace, bitmapInfo, rgbProvider,NULL,NO, renderingIntent);
  137. NSImage *rgbImage = [[NSImage alloc] initWithCGImage:rgbImageRef size: rect.size];
  138. CGImageRelease(rgbImageRef);
  139. free(rgbData);
  140. CGDataProviderRelease(rgbProvider);
  141. CGDataProviderRef alphaProvider = CGDataProviderCreateWithData(NULL, alphaData, width*height*4, NULL);
  142. CGImageRef alphaImageRef = CGImageCreate(width, height, 8, 32, 4*width,colorSpace, bitmapInfo, alphaProvider,NULL,NO, renderingIntent);
  143. NSImage *alphaImage = [[NSImage alloc] initWithCGImage:alphaImageRef size:rect.size];
  144. CGImageRelease(alphaImageRef);
  145. free(alphaData);
  146. CGDataProviderRelease(alphaProvider);
  147. NSString *alphaDir = [self.resourceDirectory stringByAppendingPathComponent:@"alphaImages"];
  148. NSString *rgbDir = [self.resourceDirectory stringByAppendingPathComponent:@"rbgImages"];
  149. [self saveImage:alphaImage atPath:[alphaDir stringByAppendingPathComponent:name]];
  150. [self saveImage:rgbImage atPath:[rgbDir stringByAppendingPathComponent:name]];
  151. }
  152. - (NSRect)alphaRectForPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  153. NSSize totalSize = [self mergedSizeWithPosition:position rgbSize:size alphaScale:scale];
  154. NSSize alphaSize = NSMakeSize(size.width*scale, size.height*scale);
  155. return [self rectForSize:alphaSize containerSize:totalSize position:position];
  156. }
  157. - (NSRect)rgbRectForPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  158. NSSize totalSize = [self mergedSizeWithPosition:position rgbSize:size alphaScale:scale];
  159. VapxAlphaPostion rgbPostion = VapxAlphaPostion_rightCenter;
  160. switch (position) {
  161. case VapxAlphaPostion_leftTop:
  162. case VapxAlphaPostion_leftCenter:
  163. case VapxAlphaPostion_leftBottom:
  164. rgbPostion = VapxAlphaPostion_rightTop;
  165. break;
  166. case VapxAlphaPostion_rightTop:
  167. case VapxAlphaPostion_rightCenter:
  168. case VapxAlphaPostion_rightBottom:
  169. rgbPostion = VapxAlphaPostion_leftTop;
  170. break;
  171. case VapxAlphaPostion_bottomLeft:
  172. case VapxAlphaPostion_bottomRight:
  173. case VapxAlphaPostion_bottomCenter:
  174. rgbPostion = VapxAlphaPostion_topLeft;
  175. break;
  176. case VapxAlphaPostion_topLeft:
  177. case VapxAlphaPostion_topRight:
  178. case VapxAlphaPostion_topCenter:
  179. rgbPostion = VapxAlphaPostion_bottomLeft;
  180. break;
  181. default:
  182. break;
  183. }
  184. return [self rectForSize:size containerSize:totalSize position:rgbPostion];
  185. }
  186. - (NSRect)rectForSize:(NSSize)size containerSize:(NSSize)containerSize position:(VapxAlphaPostion)position {
  187. CGFloat x,y;
  188. switch (position) {
  189. case VapxAlphaPostion_leftTop:
  190. x = 0;
  191. y = containerSize.height-size.height;
  192. break;
  193. case VapxAlphaPostion_leftCenter:
  194. x = 0;
  195. y = (containerSize.height-size.height)/2.0;
  196. break;
  197. case VapxAlphaPostion_leftBottom:
  198. x = 0;
  199. y = 0;
  200. break;
  201. case VapxAlphaPostion_rightTop:
  202. x = containerSize.width - size.width;
  203. y = containerSize.height-size.height;
  204. break;
  205. case VapxAlphaPostion_rightCenter:
  206. x = containerSize.width - size.width;
  207. y = (containerSize.height-size.height)/2.0;
  208. break;
  209. case VapxAlphaPostion_rightBottom:
  210. x = containerSize.width - size.width;
  211. y = 0;
  212. break;
  213. case VapxAlphaPostion_bottomLeft:
  214. x = 0;
  215. y = 0;
  216. break;
  217. case VapxAlphaPostion_bottomRight:
  218. x = containerSize.width - size.width;
  219. y = 0;
  220. break;
  221. case VapxAlphaPostion_bottomCenter:
  222. x = (containerSize.width-size.width)/2.0;
  223. y = 0;
  224. break;
  225. case VapxAlphaPostion_topLeft:
  226. x = 0;
  227. y = containerSize.height-size.height;
  228. break;
  229. case VapxAlphaPostion_topRight:
  230. x = containerSize.width - size.width;
  231. y = containerSize.height-size.height;
  232. break;
  233. case VapxAlphaPostion_topCenter:
  234. x = (containerSize.width-size.width)/2.0;
  235. y = containerSize.height-size.height;
  236. break;
  237. default:
  238. break;
  239. }
  240. return NSMakeRect(x, y, size.width, size.height);
  241. }
  242. - (NSSize)mergedSizeWithPosition:(VapxAlphaPostion)position rgbSize:(NSSize)size alphaScale:(CGFloat)scale {
  243. switch (position) {
  244. case VapxAlphaPostion_leftTop:
  245. case VapxAlphaPostion_leftCenter:
  246. case VapxAlphaPostion_leftBottom:
  247. case VapxAlphaPostion_rightTop:
  248. case VapxAlphaPostion_rightCenter:
  249. case VapxAlphaPostion_rightBottom:
  250. return NSMakeSize(size.width+size.width*scale, size.height);
  251. case VapxAlphaPostion_bottomLeft:
  252. case VapxAlphaPostion_bottomRight:
  253. case VapxAlphaPostion_bottomCenter:
  254. case VapxAlphaPostion_topLeft:
  255. case VapxAlphaPostion_topRight:
  256. case VapxAlphaPostion_topCenter:
  257. return NSMakeSize(size.width, size.height+size.height*scale);
  258. default:
  259. break;
  260. }
  261. }
  262. @end