Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github "SDWebImage/SDWebImage" ~> 5.0
github "SDWebImage/libheif-Xcode" >= 1.4.0
github "SDWebImage/SDWebImage" ~> 5.5
github "SDWebImage/libheif-Xcode" >= 1.6.0
6 changes: 3 additions & 3 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
github "SDWebImage/SDWebImage" "5.0.1"
github "SDWebImage/libde265-Xcode" "1.0.3"
github "SDWebImage/libheif-Xcode" "1.4.0"
github "SDWebImage/SDWebImage" "5.5.2"
github "SDWebImage/libde265-Xcode" "1.0.5"
github "SDWebImage/libheif-Xcode" "1.6.1"
36 changes: 18 additions & 18 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PODS:
- libde265 (1.0.3)
- libheif/libde265 (1.5.0):
- libde265 (1.0.5)
- libheif/libde265 (1.6.1):
- libde265
- libheif/libheif
- libheif/libheif (1.5.0)
- libheif/libx265 (1.5.0):
- libheif/libheif (1.6.1)
- libheif/libx265 (1.6.1):
- libheif/libheif
- libx265
- libx265 (3.0):
Expand All @@ -29,25 +29,25 @@ PODS:
- libx265/encoder
- libx265/input
- libx265/output
- SDWebImage/Core (5.1.0)
- SDWebImageHEIFCoder/libde265 (0.5.3):
- SDWebImage/Core (5.5.2)
- SDWebImageHEIFCoder/libde265 (0.6.0):
- libheif/libde265
- SDWebImage/Core (~> 5.0)
- SDWebImage/Core (~> 5.5)
- SDWebImageHEIFCoder/libheif
- SDWebImageHEIFCoder/libheif (0.5.3):
- libheif/libheif (>= 1.4.0)
- SDWebImage/Core (~> 5.0)
- SDWebImageHEIFCoder/libx265 (0.5.3):
- SDWebImageHEIFCoder/libheif (0.6.0):
- libheif/libheif (>= 1.6.0)
- SDWebImage/Core (~> 5.5)
- SDWebImageHEIFCoder/libx265 (0.6.0):
- libheif/libx265
- SDWebImage/Core (~> 5.0)
- SDWebImage/Core (~> 5.5)
- SDWebImageHEIFCoder/libheif

DEPENDENCIES:
- SDWebImageHEIFCoder/libde265 (from `../`)
- SDWebImageHEIFCoder/libx265 (from `../`)

SPEC REPOS:
https://github.com/cocoapods/specs.git:
trunk:
- libde265
- libheif
- libx265
Expand All @@ -58,12 +58,12 @@ EXTERNAL SOURCES:
:path: "../"

SPEC CHECKSUMS:
libde265: b2a0cc3d2aeaafb792b43fcf2a122f42e1de107c
libheif: 6ddc3179ff63e975ceebb65de50ba1728cc9d457
libde265: 053dea4f2438ace90617f57d10cb7ab967df3393
libheif: c104a83d861766705e5b7492d6b6ecd884457442
libx265: 23ab716aae3eff1e6b68b3db6ace8883867691c4
SDWebImage: fb387001955223213dde14bc08c8b73f371f8d8f
SDWebImageHEIFCoder: 47c82fa89aeaf95240213aa844754b441ef4de6f
SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca
SDWebImageHEIFCoder: 8a12510ff3b366e33f95a01661112afb422b9ed1

PODFILE CHECKSUM: ed097a0e3cb0df40f8e18e8e1e8b396ec38d29ff

COCOAPODS: 1.7.5
COCOAPODS: 1.8.4
9 changes: 2 additions & 7 deletions Example/SDWebImageHEIFCoder/SDAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ @implementation SDAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, *)) {
// These version supports Image/IO built-in decoding (hardware accelerate for A9+ device)
} else {
// Don't support HEIF decoding, add coder
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
}
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
return YES;
}

Expand Down
2 changes: 1 addition & 1 deletion Example/SDWebImageHEIFCoder/SDViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ - (void)viewDidLoad
[self.view addSubview:imageView1];
[self.view addSubview:imageView2];

[imageView1 sd_setImageWithURL:singleHEICURL placeholderImage:nil options:0 completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
[imageView1 sd_setImageWithURL:singleHEICURL placeholderImage:nil options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(500, 500))} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (image) {
NSLog(@"Single HEIC load success");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
Expand Down
9 changes: 2 additions & 7 deletions Example/SDWebImageHEIFCoder_Example macOS/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@ @implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, *)) {
// These version supports Image/IO built-in decoding (hardware accelerate for A9+ device)
} else {
// Don't support HEIF decoding, add coder
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
}
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
}


Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let package = Package(
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0"),
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.5.0"),
.package(url: "https://github.com/SDWebImage/libheif-Xcode.git", from: "1.6.1")
],
targets: [
Expand Down
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ let package = Package(

## Usage

### Add Coder

To use HEIF coder, you should firstly add the `SDImageHEIFCoder.sharedCoder` to the coders manager. You can also detect the target platform compatibility for HEIF and choose add coder.

+ Objective-C
Expand All @@ -112,6 +114,8 @@ if #available(iOS 11.0, macOS 10.13, tvOS 11.0, *) {
}
```

### Loading

Then you can call the View Category method to start load HEIF images.

+ Objective-C
Expand All @@ -128,6 +132,52 @@ let imageView: UIImageView
imageView.sd_setImage(with: url)
```

### Decoding

`SDImageHEIFCoder` currently supports decode the static HEIF images.

Note HEIF sequence images(.heics) is not supported currently, only supported in built-in coder from SDWebImage for iOS 13+/macOS 10.15+, also supported by [Safari and WebKit](https://bugs.webkit.org/show_bug.cgi?id=197384).

+ Objective-C

```objective-c
// HEIF image decoding
NSData *heifData;
UIImage *image = [[SDImageHEIFCoder sharedCoder] decodedImageWithData:heifData options:nil];
```

+ Swift

```swift
// HEIF image decoding
let heifData: Data
let image = SDImageHEIFCoder.shared.decodedImage(with: data, options: nil)
```

### Thumbnail Decoding (0.7.0+)

HEIF image container supports embed thumbnail image. If we can found a suitable thumbnail image, we pick that instead for quickly display, else we will decode full pixel image and scale down.

+ Objective-C

```objective-c
// HEIF thumbnail image decoding
NSData *heifData;
CGSize thumbnailSize = CGSizeMake(300, 300);
UIImage *thumbnailImage = [[SDImageHEIFCoder sharedCoder] decodedImageWithData:heifData options:@{SDImageCoderDecodeThumbnailPixelSize : @(thumbnailSize}];
```

+ Swift

```swift
// HEIF thumbnail image decoding
let heifData: Data
let thumbnailSize = CGSize(width: 300, height: 300)
let image = SDImageHEIFCoder.shared.decodedImage(with: data, options: [.decodeThumbnailPixelSize: thumbnailSize])
```

### Encoding

`SDWebImageHEIFCoder` also support HEIF encoding (need x265 subspec). You can encode `UIImage` to HEIF compressed image data.

+ Objective-C
Expand Down
4 changes: 2 additions & 2 deletions SDWebImageHEIFCoder.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Which is built based on the open-sourced libheif codec.

# HEIF core dependency
s.subspec 'libheif' do |ss|
ss.dependency 'libheif/libheif', '>= 1.4.0'
ss.dependency 'libheif/libheif', '>= 1.6.0'
ss.source_files = 'SDWebImageHEIFCoder/Classes/**/*', 'SDWebImageHEIFCoder/Module/SDWebImageHEIFCoder.h'
end

Expand All @@ -53,5 +53,5 @@ Which is built based on the open-sourced libheif codec.
}
end

s.dependency 'SDWebImage/Core', '~> 5.0'
s.dependency 'SDWebImage/Core', '~> 5.5'
end
118 changes: 109 additions & 9 deletions SDWebImageHEIFCoder/Classes/SDImageHEIFCoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ static void FreeImageData(void *info, const void *data, size_t size) {
heif_image_release(img); // `heif_image_release` will free the bitmap buffer. We do not call `free`
}

/// Calculate the actual thumnail pixel size
static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio, CGSize thumbnailSize) {
CGFloat width = fullSize.width;
CGFloat height = fullSize.height;
CGFloat resultWidth;
CGFloat resultHeight;

if (width == 0 || height == 0 || thumbnailSize.width == 0 || thumbnailSize.height == 0 || (width <= thumbnailSize.width && height <= thumbnailSize.height)) {
// Full Pixel
resultWidth = width;
resultHeight = height;
} else {
// Thumbnail
if (preserveAspectRatio) {
CGFloat pixelRatio = width / height;
CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height;
if (pixelRatio > thumbnailRatio) {
resultWidth = thumbnailSize.width;
resultHeight = ceil(thumbnailSize.width / pixelRatio);
} else {
resultHeight = thumbnailSize.height;
resultWidth = ceil(thumbnailSize.height * pixelRatio);
}
} else {
resultWidth = thumbnailSize.width;
resultHeight = thumbnailSize.height;
}
}

return CGSizeMake(resultWidth, resultHeight);
}

@implementation SDImageHEIFCoder

+ (instancetype)sharedCoder {
Expand Down Expand Up @@ -74,8 +106,24 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
}
}

CGSize thumbnailSize = CGSizeZero;
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
if (thumbnailSizeValue != nil) {
#if SD_MAC
thumbnailSize = thumbnailSizeValue.sizeValue;
#else
thumbnailSize = thumbnailSizeValue.CGSizeValue;
#endif
}

BOOL preserveAspectRatio = YES;
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
if (preserveAspectRatioValue != nil) {
preserveAspectRatio = preserveAspectRatioValue.boolValue;
}

// Currently only support primary image :)
CGImageRef imageRef = [self sd_createHEIFImageWithData:data];
CGImageRef imageRef = [self sd_createHEIFImageWithData:data thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];
if (!imageRef) {
return nil;
}
Expand All @@ -91,7 +139,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
}

// Only decode the primary image (HEIF also support tied-image and animated image)
- (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETURNS_RETAINED {
- (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data thumbnailSize:(CGSize)thumbnailSize preserveAspectRatio:(BOOL)preserveAspectRatio CF_RETURNS_RETAINED {
heif_context* ctx = heif_context_alloc();
if (!ctx) {
return nil;
Expand All @@ -113,6 +161,50 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
return nil;
}

// check thumbnail firstly
if (thumbnailSize.width > 0 && thumbnailSize.height > 0) {
heif_item_id thumbnailID;
int thumbnailCount = heif_image_handle_get_list_of_thumbnail_IDs(handle, &thumbnailID, 1);
if (thumbnailCount > 0) {
heif_image_handle *thumbnailHandle;
error = heif_image_handle_get_thumbnail(handle, thumbnailID, &thumbnailHandle);
if (error.code != heif_error_Ok) {
heif_image_handle_release(handle);
heif_context_free(ctx);
return nil;
}

// use full image to scale down if pixel size is smaller than thumbnail size
int handleWidth = heif_image_handle_get_width(handle);
int handleHeight = heif_image_handle_get_height(handle);
if (handleWidth < thumbnailSize.width && handleHeight < thumbnailSize.height) {
CGImageRef imageRef = [self sd_createFrameWithImageHandle:thumbnailHandle thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];

// clean up
heif_image_handle_release(thumbnailHandle);
heif_image_handle_release(handle);
heif_context_free(ctx);

return imageRef;
} else {
// clean up
heif_image_handle_release(thumbnailHandle);
}
}
}

CGImageRef imageRef = [self sd_createFrameWithImageHandle:handle thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];

// clean up
heif_image_handle_release(handle);
heif_context_free(ctx);

return imageRef;
}

- (nullable CGImageRef)sd_createFrameWithImageHandle:(heif_image_handle *)handle thumbnailSize:(CGSize)thumbnailSize preserveAspectRatio:(BOOL)preserveAspectRatio CF_RETURNS_RETAINED {
heif_error error;

// check alpha channel
BOOL hasAlpha = heif_image_handle_has_alpha_channel(handle);
int depth = heif_image_handle_get_chroma_bits_per_pixel(handle);
Expand All @@ -136,13 +228,25 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
heif_image* img;
error = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, NULL);
if (error.code != heif_error_Ok) {
heif_image_handle_release(handle);
heif_context_free(ctx);
return nil;
}

int handleWidth = heif_image_handle_get_width(handle);
int handleHeight = heif_image_handle_get_height(handle);
CGSize scaledSize = SDCalculateThumbnailSize(CGSizeMake(handleWidth, handleHeight), preserveAspectRatio, thumbnailSize);
// use scaling for thumbnail
if (scaledSize.width > 0 && scaledSize.height > 0 && scaledSize.width != handleWidth && scaledSize.height != handleHeight) {
heif_image *scaled_img;
error = heif_image_scale_image(img, &scaled_img, scaledSize.width, scaledSize.height, NULL);
heif_image_release(img);
if (error.code != heif_error_Ok) {
return nil;
}
img = scaled_img;
}

int width, height, stride, bitsPerPixel, bitsPerComponent;
width = heif_image_get_width(img, heif_channel_interleaved);
width = heif_image_get_width(img, heif_channel_interleaved);
height = heif_image_get_height(img, heif_channel_interleaved);
bitsPerPixel = heif_image_get_bits_per_pixel(img, heif_channel_interleaved);
bitsPerComponent = hasHighDepth ? 16 : 8;
Expand All @@ -152,8 +256,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
const uint8_t *rgba = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
if (!rgba) {
heif_image_release(img);
heif_image_handle_release(handle);
heif_context_free(ctx);
return nil;
}
CGDataProviderRef provider =
Expand All @@ -165,8 +267,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU

// clean up
CGDataProviderRelease(provider);
heif_image_handle_release(handle);
heif_context_free(ctx);

return imageRef;
}
Expand Down