SNKNinePatchImage.m 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. //
  2. // SNKNinePatchImage.m
  3. // TestImageResible
  4. //
  5. // Created by tu jinqiu on 2018/11/6.
  6. // Copyright © 2018年 tu jinqiu. All rights reserved.
  7. //
  8. #import "SNKNinePatchImage.h"
  9. #import "SNKNinePatchImageCache.h"
  10. static NSString *const kSNKNinePatchImageErrorDomain = @"kSNKNinePatchImageErrorDomain";
  11. static int s_checkCPUendian()
  12. {
  13. union
  14. {
  15. unsigned int a;
  16. unsigned char b;
  17. }c;
  18. c.a = 1;
  19. return 1 == c.b;
  20. }
  21. static uint32_t s_getBigEndian(uint32_t origin)
  22. {
  23. uint32_t newSize = origin;
  24. if (s_checkCPUendian() == 1) {
  25. newSize = ntohl(origin);
  26. }
  27. return newSize;
  28. }
  29. static BOOL s_checkDivCount(uint32_t length) {
  30. if (length == 0 || (length & 0x01) != 0) {
  31. return NO;
  32. }
  33. return YES;
  34. }
  35. static void s_scalePaddingCap(SNKNinePatchPaddingCap *paddingCap, CGFloat scale)
  36. {
  37. UIEdgeInsets padding = paddingCap->padding;
  38. padding.top = padding.top * scale;
  39. padding.left = padding.left * scale;
  40. padding.bottom = padding.bottom * scale;
  41. padding.right = padding.right * scale;
  42. paddingCap->padding = padding;
  43. UIEdgeInsets cap = paddingCap->cap;
  44. cap.top = cap.top * scale;
  45. cap.left = cap.left * scale;
  46. cap.bottom = cap.bottom * scale;
  47. cap.right = cap.right * scale;
  48. paddingCap->cap = cap;
  49. }
  50. @implementation SNKNinePatchImage
  51. + (instancetype)ninePatchImageWithName:(NSString *)name
  52. {
  53. SNKNinePatchImage *image = [[SNKNinePatchImageCache sharedCache] ninePatchImageNamed:name];
  54. if (image) {
  55. return image;
  56. }
  57. NSData *data = nil;
  58. if ([name hasSuffix:@".png"] || [name hasSuffix:@".PNG"]) {
  59. NSString *fileName = [[NSBundle mainBundle] pathForResource:name ofType:nil];
  60. data = [NSData dataWithContentsOfFile:fileName];
  61. } else {
  62. NSString *fileName = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];
  63. data = [NSData dataWithContentsOfFile:fileName];
  64. }
  65. if (data) {
  66. image = [self ninePatchImageWithImageData:data];
  67. if (image) {
  68. [[SNKNinePatchImageCache sharedCache] setNinePatchImage:image forName:name];
  69. }
  70. return image;
  71. }
  72. return nil;
  73. }
  74. + (instancetype)ninePatchImageWithImageData:(NSData *)data
  75. {
  76. return [self ninePatchImageWithImageData:data scale:1];
  77. }
  78. + (instancetype)ninePatchImageWithImageData:(NSData *)data scale:(NSInteger)scale
  79. {
  80. SNKNinePatchImage *ninePatch = [SNKNinePatchImage new];
  81. NSError *error = nil;
  82. SNKNinePatchPaddingCap paddingCap = [self p_paddingCapForPngImageData:data error:&error];
  83. if (error) {
  84. return nil;
  85. }
  86. UIImage *oldImage = [UIImage imageWithData:data];
  87. UIEdgeInsets newCap = UIEdgeInsetsMake(paddingCap.cap.top - 1, paddingCap.cap.left - 1, oldImage.size.height - paddingCap.cap.bottom, oldImage.size.width - paddingCap.cap.right);
  88. paddingCap.cap = newCap;
  89. s_scalePaddingCap(&paddingCap, 1.0 / scale);
  90. UIImage *newImage = [[UIImage alloc] initWithCGImage:oldImage.CGImage scale:scale orientation:UIImageOrientationUp];
  91. newImage = [newImage resizableImageWithCapInsets:paddingCap.cap];
  92. ninePatch.paddingCap = paddingCap;
  93. ninePatch.image = newImage;
  94. return ninePatch;
  95. }
  96. #pragma mark - private
  97. + (SNKNinePatchPaddingCap)p_paddingCapForPngImageData:(NSData *)data error:(NSError **)error
  98. {
  99. NSData *chunkData = [self p_chunkDataWithImageData:data];
  100. if (chunkData) {
  101. return [self p_paddingCapFromChunkData:chunkData error:error];
  102. } else {
  103. *error = [NSError errorWithDomain:kSNKNinePatchImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"解析ChunkData失败"}];
  104. SNKNinePatchPaddingCap ret = {};
  105. return ret;
  106. }
  107. }
  108. + (NSData *)p_chunkDataWithImageData:(NSData *)data
  109. {
  110. if (data == nil) {
  111. return nil;
  112. }
  113. #define SNKNinePatchTestDataByte(a) if (a <= 0) { return nil; }
  114. uint32_t startPos = 0;
  115. uint32_t ahead1 = [self p_getByte4FromData:data startPos:&startPos];
  116. SNKNinePatchTestDataByte(ahead1)
  117. uint32_t ahead2 = [self p_getByte4FromData:data startPos:&startPos];
  118. SNKNinePatchTestDataByte(ahead2)
  119. if (ahead1 != 0x89504e47 || ahead2 != 0x0D0A1A0A) {
  120. return nil;
  121. }
  122. while (YES) {
  123. uint32_t length = [self p_getByte4FromData:data startPos:&startPos];
  124. SNKNinePatchTestDataByte(length)
  125. uint32_t type = [self p_getByte4FromData:data startPos:&startPos];
  126. SNKNinePatchTestDataByte(type)
  127. if (type != 0x6E705463) {
  128. startPos += (length + 4);
  129. continue;
  130. }
  131. return [data subdataWithRange:NSMakeRange(startPos, length)];
  132. }
  133. return nil;
  134. #undef SNKNinePatchTestDataByte
  135. }
  136. + (SNKNinePatchPaddingCap)p_paddingCapFromChunkData:(NSData *)data error:(NSError **)error
  137. {
  138. uint32_t startPos = 0;
  139. startPos += 1;
  140. uint32_t xLength = [self p_getByteFromData:data startPos:&startPos];
  141. uint32_t yLength = [self p_getByteFromData:data startPos:&startPos];
  142. startPos += 1;
  143. BOOL xCheck = s_checkDivCount(xLength);
  144. BOOL yCheck = s_checkDivCount(yLength);
  145. NSAssert(xCheck && yCheck, @"checkDivCount failure");
  146. // skip 8 bytes
  147. startPos += 8;
  148. uint32_t left = [self p_getByte4FromData:data startPos:&startPos];
  149. uint32_t right = [self p_getByte4FromData:data startPos:&startPos];
  150. uint32_t top = [self p_getByte4FromData:data startPos:&startPos];
  151. uint32_t bottom = [self p_getByte4FromData:data startPos:&startPos];
  152. UIEdgeInsets padding = UIEdgeInsetsMake(top, left, bottom, right);
  153. // skip 4 bytes
  154. startPos += 4;
  155. uint32_t xCapArr[256];
  156. [self p_getBytesArr:xCapArr fromData:data startPos:&startPos count:xLength];
  157. uint32_t yCapArr[256];
  158. [self p_getBytesArr:yCapArr fromData:data startPos:&startPos count:yLength];
  159. uint32_t xCapStart = xCapArr[0];
  160. uint32_t xCapEnd = xCapArr[xLength - 1];
  161. uint32_t yCapStart = yCapArr[0];
  162. uint32_t yCapEnd = yCapArr[yLength - 1];
  163. UIEdgeInsets cap = UIEdgeInsetsMake(yCapStart, xCapStart, yCapEnd, xCapEnd);
  164. SNKNinePatchPaddingCap paddingCap = {padding, cap};
  165. return paddingCap;
  166. }
  167. + (uint32_t)p_getByte4FromData:(NSData *)data
  168. startPos:(uint32_t *)startPos
  169. {
  170. return [self p_getBytesFromData:data startPos:startPos length:4];
  171. }
  172. + (uint32_t)p_getByteFromData:(NSData *)data
  173. startPos:(uint32_t *)startPos
  174. {
  175. return [self p_getBytesFromData:data startPos:startPos length:1];
  176. }
  177. + (uint32_t)p_getBytesFromData:(NSData *)data
  178. startPos:(uint32_t *)startPos
  179. length:(uint32_t)length
  180. {
  181. uint32_t bytes = 0;
  182. uint32_t start = *startPos;
  183. if (start + length > data.length) {
  184. return 0;
  185. }
  186. [data getBytes:&bytes range:NSMakeRange(start, length)];
  187. if (length > 1) {
  188. bytes = s_getBigEndian(bytes);
  189. }
  190. *startPos = start + length;
  191. return bytes;
  192. }
  193. + (void)p_getBytesArr:(uint32_t *)bytesArr
  194. fromData:(NSData *)data
  195. startPos:(uint32_t *)startPos
  196. count:(uint32_t)count
  197. {
  198. for (int32_t ii = 0; ii < count; ++ii) {
  199. uint32_t bytes = [self p_getBytesFromData:data startPos:startPos length:4];
  200. bytesArr[ii] = bytes;
  201. }
  202. }
  203. @end