YYBPGCoder.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //
  2. // YYBPGCoder.m
  3. // YYKitExample
  4. //
  5. // Created by ibireme on 15/8/13.
  6. // Copyright (c) 2015 ibireme. All rights reserved.
  7. //
  8. #import "YYBPGCoder.h"
  9. #import <ImageIO/ImageIO.h>
  10. #import <Accelerate/Accelerate.h>
  11. #import <bpg/libbpg.h>
  12. #define YY_FOUR_CC(c1,c2,c3,c4) ((uint32_t)(((c4) << 24) | ((c3) << 16) | ((c2) << 8) | (c1)))
  13. /// Returns byte-aligned size.
  14. static inline size_t _YYImageByteAlign(size_t size, size_t alignment) {
  15. return ((size + (alignment - 1)) / alignment) * alignment;
  16. }
  17. /**
  18. A callback used in CGDataProviderCreateWithData() to release data.
  19. Example:
  20. void *data = malloc(size);
  21. CGDataProviderRef provider = CGDataProviderCreateWithData(data, data, size, YYCGDataProviderReleaseDataCallback);
  22. */
  23. static void _YYCGDataProviderReleaseDataCallback(void *info, const void *data, size_t size) {
  24. free(info);
  25. }
  26. CGImageRef YYCGImageCreateWithBPGData(CFDataRef bpgData, BOOL decodeForDisplay) {
  27. BPGDecoderContext *decoderContext = NULL;
  28. BPGImageInfo imageInfo = {0};
  29. size_t width, height, lineSize, stride, size;
  30. uint8_t *rgbaLine = NULL, *rgbaBuffer = NULL;
  31. CGDataProviderRef dataProvider = NULL;
  32. CGImageRef cgImage = NULL;
  33. CGBitmapInfo bitmapInfo;
  34. if (!bpgData || CFDataGetLength(bpgData) == 0) return NULL;
  35. decoderContext = bpg_decoder_open();
  36. if (!decoderContext) return NULL;
  37. if (bpg_decoder_decode(decoderContext, CFDataGetBytePtr(bpgData), (int)CFDataGetLength(bpgData)) < 0) goto fail;
  38. if (bpg_decoder_get_info(decoderContext, &imageInfo) < 0) goto fail;
  39. width = imageInfo.width;
  40. height = imageInfo.height;
  41. lineSize = 4 * width;
  42. stride = _YYImageByteAlign(lineSize, 32);
  43. size = stride * height;
  44. if (width == 0 || height == 0) goto fail;
  45. rgbaLine = malloc(lineSize);
  46. if (!rgbaLine) goto fail;
  47. rgbaBuffer = malloc(size);
  48. if (!rgbaBuffer) goto fail;
  49. if (bpg_decoder_start(decoderContext, BPG_OUTPUT_FORMAT_RGBA32) < 0) goto fail;
  50. for (int y = 0; y < height; y++) {
  51. if (bpg_decoder_get_line(decoderContext, rgbaLine) < 0) goto fail;
  52. memcpy(rgbaBuffer + (y * stride), rgbaLine, lineSize);
  53. }
  54. free(rgbaLine);
  55. rgbaLine = NULL;
  56. bpg_decoder_close(decoderContext);
  57. decoderContext = NULL;
  58. if (decodeForDisplay) {
  59. vImage_Buffer src;
  60. src.data = rgbaBuffer;
  61. src.width = width;
  62. src.height = height;
  63. src.rowBytes = stride;
  64. vImage_Error error;
  65. // premultiply RGBA
  66. error = vImagePremultiplyData_RGBA8888(&src, &src, kvImageNoFlags);
  67. if (error != kvImageNoError) goto fail;
  68. // convert to bgrA
  69. uint8_t map[4] = {2,1,0,3};
  70. error = vImagePermuteChannels_ARGB8888(&src, &src, map, kvImageNoFlags);
  71. if (error != kvImageNoError) goto fail;
  72. bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
  73. } else {
  74. bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrderDefault;
  75. }
  76. dataProvider = CGDataProviderCreateWithData(rgbaBuffer, rgbaBuffer, size, _YYCGDataProviderReleaseDataCallback);
  77. if (!dataProvider) goto fail;
  78. rgbaBuffer = NULL; // hold by provider
  79. cgImage = CGImageCreate(width, height, 8, 32, stride, YYCGColorSpaceGetDeviceRGB(),
  80. bitmapInfo, dataProvider, NULL, NO,
  81. kCGRenderingIntentDefault);
  82. CGDataProviderRelease(dataProvider);
  83. return cgImage;
  84. fail:
  85. if (decoderContext) bpg_decoder_close(decoderContext);
  86. if (rgbaLine) free(rgbaLine);
  87. if (rgbaBuffer) free(rgbaBuffer);
  88. return NULL;
  89. }
  90. CGImageRef YYCGImageCreateFrameWithBPGData(CFDataRef bpgData, NSUInteger frameIndex, BOOL decodeForDisplay) {
  91. BPGDecoderContext *decoderContext = NULL;
  92. BPGImageInfo imageInfo = {0};
  93. size_t width, height, lineSize, stride, size;
  94. uint8_t *rgbaLine = NULL, *rgbaBuffer = NULL;
  95. CGDataProviderRef dataProvider = NULL;
  96. CGImageRef cgImage = NULL;
  97. CGBitmapInfo bitmapInfo;
  98. if (!bpgData || CFDataGetLength(bpgData) == 0) return NULL;
  99. decoderContext = bpg_decoder_open();
  100. if (!decoderContext) return NULL;
  101. if (bpg_decoder_decode(decoderContext, CFDataGetBytePtr(bpgData), (int)CFDataGetLength(bpgData)) < 0) goto fail;
  102. if (bpg_decoder_get_info(decoderContext, &imageInfo) < 0) goto fail;
  103. width = imageInfo.width;
  104. height = imageInfo.height;
  105. lineSize = 4 * width;
  106. stride = _YYImageByteAlign(lineSize, 32);
  107. size = stride * height;
  108. if (width == 0 || height == 0) goto fail;
  109. rgbaLine = malloc(lineSize);
  110. if (!rgbaLine) goto fail;
  111. rgbaBuffer = malloc(size);
  112. if (!rgbaBuffer) goto fail;
  113. for (NSUInteger i = 0; i <= frameIndex; i++) {
  114. if (bpg_decoder_start(decoderContext, BPG_OUTPUT_FORMAT_RGBA32) < 0) goto fail;
  115. }
  116. for (int y = 0; y < height; y++) {
  117. if (bpg_decoder_get_line(decoderContext, rgbaLine) < 0) goto fail;
  118. memcpy(rgbaBuffer + (y * stride), rgbaLine, lineSize);
  119. }
  120. free(rgbaLine);
  121. rgbaLine = NULL;
  122. bpg_decoder_close(decoderContext);
  123. decoderContext = NULL;
  124. if (decodeForDisplay) {
  125. vImage_Buffer src;
  126. src.data = rgbaBuffer;
  127. src.width = width;
  128. src.height = height;
  129. src.rowBytes = stride;
  130. vImage_Error error;
  131. // premultiply RGBA
  132. error = vImagePremultiplyData_RGBA8888(&src, &src, kvImageNoFlags);
  133. if (error != kvImageNoError) goto fail;
  134. // convert to BGRA
  135. uint8_t map[4] = {2,1,0,3};
  136. error = vImagePermuteChannels_ARGB8888(&src, &src, map, kvImageNoFlags);
  137. if (error != kvImageNoError) goto fail;
  138. bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
  139. } else {
  140. bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrderDefault;
  141. }
  142. dataProvider = CGDataProviderCreateWithData(rgbaBuffer, rgbaBuffer, size, _YYCGDataProviderReleaseDataCallback);
  143. if (!dataProvider) goto fail;
  144. rgbaBuffer = NULL; // hold by provider
  145. cgImage = CGImageCreate(width, height, 8, 32, stride, YYCGColorSpaceGetDeviceRGB(),
  146. bitmapInfo, dataProvider, NULL, NO,
  147. kCGRenderingIntentDefault);
  148. CGDataProviderRelease(dataProvider);
  149. return cgImage;
  150. fail:
  151. if (decoderContext) bpg_decoder_close(decoderContext);
  152. if (rgbaLine) free(rgbaLine);
  153. if (rgbaBuffer) free(rgbaBuffer);
  154. return NULL;
  155. }
  156. void YYCGImageDecodeAllFrameInBPGData(CFDataRef bpgData, BOOL decodeForDisplay) {
  157. BPGDecoderContext *decoderContext = NULL;
  158. BPGImageInfo imageInfo = {0};
  159. size_t width, height, lineSize, stride, size;
  160. uint8_t *rgbaLine = NULL, *rgbaBuffer = NULL;
  161. CGDataProviderRef dataProvider = NULL;
  162. CGImageRef cgImage = NULL;
  163. CGBitmapInfo bitmapInfo;
  164. if (!bpgData || CFDataGetLength(bpgData) == 0) return;
  165. decoderContext = bpg_decoder_open();
  166. if (!decoderContext) return;
  167. if (bpg_decoder_decode(decoderContext, CFDataGetBytePtr(bpgData), (int)CFDataGetLength(bpgData)) < 0) goto end;
  168. if (bpg_decoder_get_info(decoderContext, &imageInfo) < 0) goto end;
  169. width = imageInfo.width;
  170. height = imageInfo.height;
  171. lineSize = 4 * width;
  172. stride = _YYImageByteAlign(lineSize, 32);
  173. size = stride * height;
  174. for (;;) {
  175. if (bpg_decoder_start(decoderContext, BPG_OUTPUT_FORMAT_RGBA32) < 0) goto end;
  176. if (width == 0 || height == 0) goto end;
  177. rgbaLine = malloc(lineSize);
  178. if (!rgbaLine) goto end;
  179. rgbaBuffer = malloc(size);
  180. if (!rgbaBuffer) goto end;
  181. for (int y = 0; y < height; y++) {
  182. if (bpg_decoder_get_line(decoderContext, rgbaLine) < 0) goto end;
  183. memcpy(rgbaBuffer + (y * stride), rgbaLine, lineSize);
  184. }
  185. free(rgbaLine);
  186. rgbaLine = NULL;
  187. if (decodeForDisplay) {
  188. vImage_Buffer src;
  189. src.data = rgbaBuffer;
  190. src.width = width;
  191. src.height = height;
  192. src.rowBytes = stride;
  193. vImage_Error error;
  194. // premultiply RGBA
  195. error = vImagePremultiplyData_RGBA8888(&src, &src, kvImageNoFlags);
  196. if (error != kvImageNoError) goto end;
  197. // convert to BGRA
  198. uint8_t map[4] = {2,1,0,3};
  199. error = vImagePermuteChannels_ARGB8888(&src, &src, map, kvImageNoFlags);
  200. if (error != kvImageNoError) goto end;
  201. bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
  202. } else {
  203. bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrderDefault;
  204. }
  205. dataProvider = CGDataProviderCreateWithData(rgbaBuffer, rgbaBuffer, size, _YYCGDataProviderReleaseDataCallback);
  206. if (!dataProvider) goto end;
  207. rgbaBuffer = NULL; // hold by provider
  208. cgImage = CGImageCreate(width, height, 8, 32, stride, YYCGColorSpaceGetDeviceRGB(),
  209. bitmapInfo, dataProvider, NULL, NO,
  210. kCGRenderingIntentDefault);
  211. CGDataProviderRelease(dataProvider);
  212. if (cgImage) CFRelease(cgImage);
  213. }
  214. return;
  215. end:
  216. if (decoderContext) bpg_decoder_close(decoderContext);
  217. if (rgbaLine) free(rgbaLine);
  218. if (rgbaBuffer) free(rgbaBuffer);
  219. return;
  220. }
  221. BOOL YYImageIsBPGData(CFDataRef data) {
  222. if (!data || CFDataGetLength(data) < 8) return NO;
  223. const uint8_t *bytes = CFDataGetBytePtr(data);
  224. uint32_t magic = *((uint32_t *)bytes);
  225. return magic == YY_FOUR_CC('B', 'P', 'G', 0xFB);
  226. }