SDImageCoderHelper.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * This file is part of the SDWebImage package.
  3. * (c) Olivier Poitrey <rs@dailymotion.com>
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. #import <ImageIO/ImageIO.h>
  9. #import "SDWebImageCompat.h"
  10. #import "SDImageFrame.h"
  11. /// The options controls how we force pre-draw the image (to avoid lazy-decoding). Which need OS's framework compatibility
  12. typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
  13. /// automatically choose the solution based on image format, hardware, OS version. This keep balance for compatibility and performance. Default after SDWebImage 5.13.0
  14. SDImageCoderDecodeSolutionAutomatic,
  15. /// always use CoreGraphics to draw on bitmap context and trigger decode. Best compatibility. Default before SDWebImage 5.13.0
  16. SDImageCoderDecodeSolutionCoreGraphics,
  17. /// available on iOS/tvOS 15+, use UIKit's new CGImageDecompressor/CMPhoto to decode. Best performance. If failed, will fallback to CoreGraphics as well
  18. SDImageCoderDecodeSolutionUIKit
  19. };
  20. /// The policy to force-decode the origin CGImage (produced by Image Coder Plugin)
  21. /// Some CGImage may be lazy, or not lazy, but need extra copy to render on screen
  22. /// The force-decode step help to `pre-process` to get the best suitable CGImage to render, which can increase frame rate
  23. /// The downside is that force-decode may consume RAM and CPU, and may loss the `lazy` support (lazy CGImage can be purged when memory warning, and re-created if need), see more: `SDImageCoderDecodeUseLazyDecoding`
  24. typedef NS_ENUM(NSUInteger, SDImageForceDecodePolicy) {
  25. /// Based on input CGImage's colorspace, alignment, bitmapinfo, if it may trigger `CA::copy_image` extra copy, we will force-decode, else don't
  26. SDImageForceDecodePolicyAutomatic,
  27. /// Never force decode input CGImage
  28. SDImageForceDecodePolicyNever,
  29. /// Always force decode input CGImage (only once)
  30. SDImageForceDecodePolicyAlways
  31. };
  32. /// Byte alignment the bytes size with alignment
  33. /// - Parameters:
  34. /// - size: The bytes size
  35. /// - alignment: The alignment, in bytes
  36. static inline size_t SDByteAlign(size_t size, size_t alignment) {
  37. return ((size + (alignment - 1)) / alignment) * alignment;
  38. }
  39. /// The pixel format about the information to call `CGImageCreate` suitable for current hardware rendering
  40. typedef struct SDImagePixelFormat {
  41. /// Typically is pre-multiplied RGBA8888 for alpha image, RGBX8888 for non-alpha image.
  42. CGBitmapInfo bitmapInfo;
  43. /// Typically is 32, the 8 pixels bytesPerRow.
  44. size_t alignment;
  45. } SDImagePixelFormat;
  46. /**
  47. Provide some common helper methods for building the image decoder/encoder.
  48. */
  49. @interface SDImageCoderHelper : NSObject
  50. /**
  51. Return an animated image with frames array.
  52. For UIKit, this will apply the patch and then create animated UIImage. The patch is because that `+[UIImage animatedImageWithImages:duration:]` just use the average of duration for each image. So it will not work if different frame has different duration. Therefore we repeat the specify frame for specify times to let it work.
  53. For AppKit, NSImage does not support animates other than GIF. This will try to encode the frames to GIF format and then create an animated NSImage for rendering. Attention the animated image may loss some detail if the input frames contain full alpha channel because GIF only supports 1 bit alpha channel. (For 1 pixel, either transparent or not)
  54. @param frames The frames array. If no frames or frames is empty, return nil
  55. @return A animated image for rendering on UIImageView(UIKit) or NSImageView(AppKit)
  56. */
  57. + (UIImage * _Nullable)animatedImageWithFrames:(NSArray<SDImageFrame *> * _Nullable)frames;
  58. /**
  59. Return frames array from an animated image.
  60. For UIKit, this will unapply the patch for the description above and then create frames array. This will also work for normal animated UIImage.
  61. For AppKit, NSImage does not support animates other than GIF. This will try to decode the GIF imageRep and then create frames array.
  62. @param animatedImage A animated image. If it's not animated, return nil
  63. @return The frames array
  64. */
  65. + (NSArray<SDImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage NS_SWIFT_NAME(frames(from:));
  66. #pragma mark - Preferred Rendering Format
  67. /// For coders who use `CGImageCreate`, use the information below to create an effient CGImage which can be render on GPU without Core Animation's extra copy (`CA::Render::copy_image`), which can be debugged using `Color Copied Image` in Xcode Instruments
  68. /// `CGImageCreate`'s `bytesPerRow`, `space`, `bitmapInfo` params should use the information below.
  69. /**
  70. Return the shared device-dependent RGB color space. This follows The Get Rule.
  71. Because it's shared, you should not retain or release this object.
  72. Typically is sRGB for iOS, screen color space (like Color LCD) for macOS.
  73. @return The device-dependent RGB color space
  74. */
  75. + (CGColorSpaceRef _Nonnull)colorSpaceGetDeviceRGB CF_RETURNS_NOT_RETAINED;
  76. /**
  77. Tthis returns the pixel format **Preferred from current hardward && OS using runtime detection**
  78. @param containsAlpha Whether the image to render contains alpha channel
  79. */
  80. + (SDImagePixelFormat)preferredPixelFormat:(BOOL)containsAlpha;
  81. /**
  82. Check whether CGImage is hardware supported to rendering on screen, without the trigger of `CA::Render::copy_image`
  83. You can debug the copied image by using Xcode's `Color Copied Image`, the copied image will turn Cyan and occupy double RAM for bitmap buffer.
  84. Typically, when the CGImage's using the method above (`colorspace` / `alignment` / `bitmapInfo`) can render withtout the copy.
  85. */
  86. + (BOOL)CGImageIsHardwareSupported:(_Nonnull CGImageRef)cgImage;
  87. /**
  88. Check whether CGImage contains alpha channel.
  89. @param cgImage The CGImage
  90. @return Return YES if CGImage contains alpha channel, otherwise return NO
  91. */
  92. + (BOOL)CGImageContainsAlpha:(_Nonnull CGImageRef)cgImage;
  93. /**
  94. Create a decoded CGImage by the provided CGImage. This follows The Create Rule and you are response to call release after usage.
  95. It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView.
  96. @note This actually call `CGImageCreateDecoded:orientation:` with the Up orientation.
  97. @param cgImage The CGImage
  98. @return A new created decoded image
  99. */
  100. + (CGImageRef _Nullable)CGImageCreateDecoded:(_Nonnull CGImageRef)cgImage CF_RETURNS_RETAINED;
  101. /**
  102. Create a decoded CGImage by the provided CGImage and orientation. This follows The Create Rule and you are response to call release after usage.
  103. It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView.
  104. @param cgImage The CGImage
  105. @param orientation The EXIF image orientation.
  106. @return A new created decoded image
  107. */
  108. + (CGImageRef _Nullable)CGImageCreateDecoded:(_Nonnull CGImageRef)cgImage orientation:(CGImagePropertyOrientation)orientation CF_RETURNS_RETAINED;
  109. /**
  110. Create a scaled CGImage by the provided CGImage and size. This follows The Create Rule and you are response to call release after usage.
  111. It will detect whether the image size matching the scale size, if not, stretch the image to the target size.
  112. @note If you need to keep aspect ratio, you can calculate the scale size by using `scaledSizeWithImageSize` first.
  113. @param cgImage The CGImage
  114. @param size The scale size in pixel.
  115. @return A new created scaled image
  116. */
  117. + (CGImageRef _Nullable)CGImageCreateScaled:(_Nonnull CGImageRef)cgImage size:(CGSize)size CF_RETURNS_RETAINED;
  118. /** Scale the image size based on provided scale size, whether or not to preserve aspect ratio, whether or not to scale up.
  119. @note For example, if you implements thumnail decoding, pass `shouldScaleUp` to NO to avoid the calculated size larger than image size.
  120. @param imageSize The image size (in pixel or point defined by caller)
  121. @param scaleSize The scale size (in pixel or point defined by caller)
  122. @param preserveAspectRatio Whether or not to preserve aspect ratio
  123. @param shouldScaleUp Whether or not to scale up (or scale down only)
  124. */
  125. + (CGSize)scaledSizeWithImageSize:(CGSize)imageSize scaleSize:(CGSize)scaleSize preserveAspectRatio:(BOOL)preserveAspectRatio shouldScaleUp:(BOOL)shouldScaleUp;
  126. /// Calculate the limited image size with the bytes, when using `SDImageCoderDecodeScaleDownLimitBytes`. This preserve aspect ratio and never scale up
  127. /// @param imageSize The image size (in pixel or point defined by caller)
  128. /// @param limitBytes The limit bytes
  129. /// @param bytesPerPixel The bytes per pixel
  130. /// @param frameCount The image frame count, 0 means 1 frame as well
  131. + (CGSize)scaledSizeWithImageSize:(CGSize)imageSize limitBytes:(NSUInteger)limitBytes bytesPerPixel:(NSUInteger)bytesPerPixel frameCount:(NSUInteger)frameCount;
  132. /**
  133. Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image. On iOS 15+, this may use `UIImage.preparingForDisplay()` to use CMPhoto for better performance than the old solution.
  134. @param image The image to be decoded
  135. @note This translate to `decodedImageWithImage:policy:` with automatic policy
  136. @return The decoded image
  137. */
  138. + (UIImage * _Nullable)decodedImageWithImage:(UIImage * _Nullable)image;
  139. /**
  140. Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image. On iOS 15+, this may use `UIImage.preparingForDisplay()` to use CMPhoto for better performance than the old solution.
  141. @param image The image to be decoded
  142. @param policy The force decode policy to decode image, will effect the check whether input image need decode
  143. @return The decoded image
  144. */
  145. + (UIImage * _Nullable)decodedImageWithImage:(UIImage * _Nullable)image policy:(SDImageForceDecodePolicy)policy;
  146. /**
  147. Return the decoded and probably scaled down image by the provided image. If the image pixels bytes size large than the limit bytes, will try to scale down. Or just works as `decodedImageWithImage:`, never scale up.
  148. @warning You should not pass too small bytes, the suggestion value should be larger than 1MB. Even we use Tile Decoding to avoid OOM, however, small bytes will consume much more CPU time because we need to iterate more times to draw each tile.
  149. @param image The image to be decoded and scaled down
  150. @param bytes The limit bytes size. Provide 0 to use the build-in limit.
  151. @note This translate to `decodedAndScaledDownImageWithImage:limitBytes:policy:` with automatic policy
  152. @return The decoded and probably scaled down image
  153. */
  154. + (UIImage * _Nullable)decodedAndScaledDownImageWithImage:(UIImage * _Nullable)image limitBytes:(NSUInteger)bytes;
  155. /**
  156. Return the decoded and probably scaled down image by the provided image. If the image pixels bytes size large than the limit bytes, will try to scale down. Or just works as `decodedImageWithImage:`, never scale up.
  157. @warning You should not pass too small bytes, the suggestion value should be larger than 1MB. Even we use Tile Decoding to avoid OOM, however, small bytes will consume much more CPU time because we need to iterate more times to draw each tile.
  158. @param image The image to be decoded and scaled down
  159. @param bytes The limit bytes size. Provide 0 to use the build-in limit.
  160. @param policy The force decode policy to decode image, will effect the check whether input image need decode
  161. @return The decoded and probably scaled down image
  162. */
  163. + (UIImage * _Nullable)decodedAndScaledDownImageWithImage:(UIImage * _Nullable)image limitBytes:(NSUInteger)bytes policy:(SDImageForceDecodePolicy)policy;
  164. /**
  165. Control the default force decode solution. Available solutions in `SDImageCoderDecodeSolution`.
  166. @note Defaults to `SDImageCoderDecodeSolutionAutomatic`, which prefers to use UIKit for JPEG/HEIF, and fallback on CoreGraphics. If you want control on your hand, set the other solution.
  167. */
  168. @property (class, readwrite) SDImageCoderDecodeSolution defaultDecodeSolution;
  169. /**
  170. Control the default limit bytes to scale down largest images.
  171. This value must be larger than 4 Bytes (at least 1x1 pixel). Defaults to 60MB on iOS/tvOS, 90MB on macOS, 30MB on watchOS.
  172. */
  173. @property (class, readwrite) NSUInteger defaultScaleDownLimitBytes;
  174. #if SD_UIKIT || SD_WATCH
  175. /**
  176. Convert an EXIF image orientation to an iOS one.
  177. @param exifOrientation EXIF orientation
  178. @return iOS orientation
  179. */
  180. + (UIImageOrientation)imageOrientationFromEXIFOrientation:(CGImagePropertyOrientation)exifOrientation NS_SWIFT_NAME(imageOrientation(from:));
  181. /**
  182. Convert an iOS orientation to an EXIF image orientation.
  183. @param imageOrientation iOS orientation
  184. @return EXIF orientation
  185. */
  186. + (CGImagePropertyOrientation)exifOrientationFromImageOrientation:(UIImageOrientation)imageOrientation;
  187. #endif
  188. @end