Skip to content

Commit 6d4d14e

Browse files
authored
Merge pull request #16 from SDWebImage/feature_thumbnail_decoding
HEIFCoder now supports the thumbnail decoding
2 parents b9035df + 5494eb9 commit 6d4d14e

File tree

10 files changed

+190
-50
lines changed

10 files changed

+190
-50
lines changed

Cartfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github "SDWebImage/SDWebImage" ~> 5.0
2-
github "SDWebImage/libheif-Xcode" >= 1.4.0
1+
github "SDWebImage/SDWebImage" ~> 5.5
2+
github "SDWebImage/libheif-Xcode" >= 1.6.0

Cartfile.resolved

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
github "SDWebImage/SDWebImage" "5.0.1"
2-
github "SDWebImage/libde265-Xcode" "1.0.3"
3-
github "SDWebImage/libheif-Xcode" "1.4.0"
1+
github "SDWebImage/SDWebImage" "5.5.2"
2+
github "SDWebImage/libde265-Xcode" "1.0.5"
3+
github "SDWebImage/libheif-Xcode" "1.6.1"

Example/Podfile.lock

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
PODS:
2-
- libde265 (1.0.3)
3-
- libheif/libde265 (1.5.0):
2+
- libde265 (1.0.5)
3+
- libheif/libde265 (1.6.1):
44
- libde265
55
- libheif/libheif
6-
- libheif/libheif (1.5.0)
7-
- libheif/libx265 (1.5.0):
6+
- libheif/libheif (1.6.1)
7+
- libheif/libx265 (1.6.1):
88
- libheif/libheif
99
- libx265
1010
- libx265 (3.0):
@@ -29,25 +29,25 @@ PODS:
2929
- libx265/encoder
3030
- libx265/input
3131
- libx265/output
32-
- SDWebImage/Core (5.1.0)
33-
- SDWebImageHEIFCoder/libde265 (0.5.3):
32+
- SDWebImage/Core (5.5.2)
33+
- SDWebImageHEIFCoder/libde265 (0.6.0):
3434
- libheif/libde265
35-
- SDWebImage/Core (~> 5.0)
35+
- SDWebImage/Core (~> 5.5)
3636
- SDWebImageHEIFCoder/libheif
37-
- SDWebImageHEIFCoder/libheif (0.5.3):
38-
- libheif/libheif (>= 1.4.0)
39-
- SDWebImage/Core (~> 5.0)
40-
- SDWebImageHEIFCoder/libx265 (0.5.3):
37+
- SDWebImageHEIFCoder/libheif (0.6.0):
38+
- libheif/libheif (>= 1.6.0)
39+
- SDWebImage/Core (~> 5.5)
40+
- SDWebImageHEIFCoder/libx265 (0.6.0):
4141
- libheif/libx265
42-
- SDWebImage/Core (~> 5.0)
42+
- SDWebImage/Core (~> 5.5)
4343
- SDWebImageHEIFCoder/libheif
4444

4545
DEPENDENCIES:
4646
- SDWebImageHEIFCoder/libde265 (from `../`)
4747
- SDWebImageHEIFCoder/libx265 (from `../`)
4848

4949
SPEC REPOS:
50-
https://github.com/cocoapods/specs.git:
50+
trunk:
5151
- libde265
5252
- libheif
5353
- libx265
@@ -58,12 +58,12 @@ EXTERNAL SOURCES:
5858
:path: "../"
5959

6060
SPEC CHECKSUMS:
61-
libde265: b2a0cc3d2aeaafb792b43fcf2a122f42e1de107c
62-
libheif: 6ddc3179ff63e975ceebb65de50ba1728cc9d457
61+
libde265: 053dea4f2438ace90617f57d10cb7ab967df3393
62+
libheif: c104a83d861766705e5b7492d6b6ecd884457442
6363
libx265: 23ab716aae3eff1e6b68b3db6ace8883867691c4
64-
SDWebImage: fb387001955223213dde14bc08c8b73f371f8d8f
65-
SDWebImageHEIFCoder: 47c82fa89aeaf95240213aa844754b441ef4de6f
64+
SDWebImage: 4d5c027c935438f341ed33dbac53ff9f479922ca
65+
SDWebImageHEIFCoder: 8a12510ff3b366e33f95a01661112afb422b9ed1
6666

6767
PODFILE CHECKSUM: ed097a0e3cb0df40f8e18e8e1e8b396ec38d29ff
6868

69-
COCOAPODS: 1.7.5
69+
COCOAPODS: 1.8.4

Example/SDWebImageHEIFCoder/SDAppDelegate.m

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,8 @@ @implementation SDAppDelegate
1414
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
1515
{
1616
// Override point for customization after application launch.
17-
if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, *)) {
18-
// These version supports Image/IO built-in decoding (hardware accelerate for A9+ device)
19-
} else {
20-
// Don't support HEIF decoding, add coder
21-
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
22-
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
23-
}
17+
SDImageHEIFCoder *HEIFCoder = [SDImageHEIFCoder sharedCoder];
18+
[[SDImageCodersManager sharedManager] addCoder:HEIFCoder];
2419
return YES;
2520
}
2621

Example/SDWebImageHEIFCoder/SDViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ - (void)viewDidLoad
3131
[self.view addSubview:imageView1];
3232
[self.view addSubview:imageView2];
3333

34-
[imageView1 sd_setImageWithURL:singleHEICURL placeholderImage:nil options:0 completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
34+
[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) {
3535
if (image) {
3636
NSLog(@"Single HEIC load success");
3737
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

Example/SDWebImageHEIFCoder_Example macOS/AppDelegate.m

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,8 @@ @implementation AppDelegate
1717

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

2924

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let package = Package(
1717
dependencies: [
1818
// Dependencies declare other packages that this package depends on.
1919
// .package(url: /* package url */, from: "1.0.0"),
20-
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0"),
20+
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.5.0"),
2121
.package(url: "https://github.com/SDWebImage/libheif-Xcode.git", from: "1.6.1")
2222
],
2323
targets: [

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ let package = Package(
8686

8787
## Usage
8888

89+
### Add Coder
90+
8991
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.
9092

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

117+
### Loading
118+
115119
Then you can call the View Category method to start load HEIF images.
116120

117121
+ Objective-C
@@ -128,6 +132,52 @@ let imageView: UIImageView
128132
imageView.sd_setImage(with: url)
129133
```
130134

135+
### Decoding
136+
137+
`SDImageHEIFCoder` currently supports decode the static HEIF images.
138+
139+
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).
140+
141+
+ Objective-C
142+
143+
```objective-c
144+
// HEIF image decoding
145+
NSData *heifData;
146+
UIImage *image = [[SDImageHEIFCoder sharedCoder] decodedImageWithData:heifData options:nil];
147+
```
148+
149+
+ Swift
150+
151+
```swift
152+
// HEIF image decoding
153+
let heifData: Data
154+
let image = SDImageHEIFCoder.shared.decodedImage(with: data, options: nil)
155+
```
156+
157+
### Thumbnail Decoding (0.7.0+)
158+
159+
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.
160+
161+
+ Objective-C
162+
163+
```objective-c
164+
// HEIF thumbnail image decoding
165+
NSData *heifData;
166+
CGSize thumbnailSize = CGSizeMake(300, 300);
167+
UIImage *thumbnailImage = [[SDImageHEIFCoder sharedCoder] decodedImageWithData:heifData options:@{SDImageCoderDecodeThumbnailPixelSize : @(thumbnailSize}];
168+
```
169+
170+
+ Swift
171+
172+
```swift
173+
// HEIF thumbnail image decoding
174+
let heifData: Data
175+
let thumbnailSize = CGSize(width: 300, height: 300)
176+
let image = SDImageHEIFCoder.shared.decodedImage(with: data, options: [.decodeThumbnailPixelSize: thumbnailSize])
177+
```
178+
179+
### Encoding
180+
131181
`SDWebImageHEIFCoder` also support HEIF encoding (need x265 subspec). You can encode `UIImage` to HEIF compressed image data.
132182

133183
+ Objective-C

SDWebImageHEIFCoder.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Which is built based on the open-sourced libheif codec.
3131

3232
# HEIF core dependency
3333
s.subspec 'libheif' do |ss|
34-
ss.dependency 'libheif/libheif', '>= 1.4.0'
34+
ss.dependency 'libheif/libheif', '>= 1.6.0'
3535
ss.source_files = 'SDWebImageHEIFCoder/Classes/**/*', 'SDWebImageHEIFCoder/Module/SDWebImageHEIFCoder.h'
3636
end
3737

@@ -53,5 +53,5 @@ Which is built based on the open-sourced libheif codec.
5353
}
5454
end
5555

56-
s.dependency 'SDWebImage/Core', '~> 5.0'
56+
s.dependency 'SDWebImage/Core', '~> 5.5'
5757
end

SDWebImageHEIFCoder/Classes/SDImageHEIFCoder.m

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,38 @@ static void FreeImageData(void *info, const void *data, size_t size) {
4343
heif_image_release(img); // `heif_image_release` will free the bitmap buffer. We do not call `free`
4444
}
4545

46+
/// Calculate the actual thumnail pixel size
47+
static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio, CGSize thumbnailSize) {
48+
CGFloat width = fullSize.width;
49+
CGFloat height = fullSize.height;
50+
CGFloat resultWidth;
51+
CGFloat resultHeight;
52+
53+
if (width == 0 || height == 0 || thumbnailSize.width == 0 || thumbnailSize.height == 0 || (width <= thumbnailSize.width && height <= thumbnailSize.height)) {
54+
// Full Pixel
55+
resultWidth = width;
56+
resultHeight = height;
57+
} else {
58+
// Thumbnail
59+
if (preserveAspectRatio) {
60+
CGFloat pixelRatio = width / height;
61+
CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height;
62+
if (pixelRatio > thumbnailRatio) {
63+
resultWidth = thumbnailSize.width;
64+
resultHeight = ceil(thumbnailSize.width / pixelRatio);
65+
} else {
66+
resultHeight = thumbnailSize.height;
67+
resultWidth = ceil(thumbnailSize.height * pixelRatio);
68+
}
69+
} else {
70+
resultWidth = thumbnailSize.width;
71+
resultHeight = thumbnailSize.height;
72+
}
73+
}
74+
75+
return CGSizeMake(resultWidth, resultHeight);
76+
}
77+
4678
@implementation SDImageHEIFCoder
4779

4880
+ (instancetype)sharedCoder {
@@ -74,8 +106,24 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
74106
}
75107
}
76108

109+
CGSize thumbnailSize = CGSizeZero;
110+
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
111+
if (thumbnailSizeValue != nil) {
112+
#if SD_MAC
113+
thumbnailSize = thumbnailSizeValue.sizeValue;
114+
#else
115+
thumbnailSize = thumbnailSizeValue.CGSizeValue;
116+
#endif
117+
}
118+
119+
BOOL preserveAspectRatio = YES;
120+
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
121+
if (preserveAspectRatioValue != nil) {
122+
preserveAspectRatio = preserveAspectRatioValue.boolValue;
123+
}
124+
77125
// Currently only support primary image :)
78-
CGImageRef imageRef = [self sd_createHEIFImageWithData:data];
126+
CGImageRef imageRef = [self sd_createHEIFImageWithData:data thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];
79127
if (!imageRef) {
80128
return nil;
81129
}
@@ -91,7 +139,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
91139
}
92140

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

164+
// check thumbnail firstly
165+
if (thumbnailSize.width > 0 && thumbnailSize.height > 0) {
166+
heif_item_id thumbnailID;
167+
int thumbnailCount = heif_image_handle_get_list_of_thumbnail_IDs(handle, &thumbnailID, 1);
168+
if (thumbnailCount > 0) {
169+
heif_image_handle *thumbnailHandle;
170+
error = heif_image_handle_get_thumbnail(handle, thumbnailID, &thumbnailHandle);
171+
if (error.code != heif_error_Ok) {
172+
heif_image_handle_release(handle);
173+
heif_context_free(ctx);
174+
return nil;
175+
}
176+
177+
// use full image to scale down if pixel size is smaller than thumbnail size
178+
int handleWidth = heif_image_handle_get_width(handle);
179+
int handleHeight = heif_image_handle_get_height(handle);
180+
if (handleWidth < thumbnailSize.width && handleHeight < thumbnailSize.height) {
181+
CGImageRef imageRef = [self sd_createFrameWithImageHandle:thumbnailHandle thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];
182+
183+
// clean up
184+
heif_image_handle_release(thumbnailHandle);
185+
heif_image_handle_release(handle);
186+
heif_context_free(ctx);
187+
188+
return imageRef;
189+
} else {
190+
// clean up
191+
heif_image_handle_release(thumbnailHandle);
192+
}
193+
}
194+
}
195+
196+
CGImageRef imageRef = [self sd_createFrameWithImageHandle:handle thumbnailSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];
197+
198+
// clean up
199+
heif_image_handle_release(handle);
200+
heif_context_free(ctx);
201+
202+
return imageRef;
203+
}
204+
205+
- (nullable CGImageRef)sd_createFrameWithImageHandle:(heif_image_handle *)handle thumbnailSize:(CGSize)thumbnailSize preserveAspectRatio:(BOOL)preserveAspectRatio CF_RETURNS_RETAINED {
206+
heif_error error;
207+
116208
// check alpha channel
117209
BOOL hasAlpha = heif_image_handle_has_alpha_channel(handle);
118210
int depth = heif_image_handle_get_chroma_bits_per_pixel(handle);
@@ -136,13 +228,25 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
136228
heif_image* img;
137229
error = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, NULL);
138230
if (error.code != heif_error_Ok) {
139-
heif_image_handle_release(handle);
140-
heif_context_free(ctx);
141231
return nil;
142232
}
143233

234+
int handleWidth = heif_image_handle_get_width(handle);
235+
int handleHeight = heif_image_handle_get_height(handle);
236+
CGSize scaledSize = SDCalculateThumbnailSize(CGSizeMake(handleWidth, handleHeight), preserveAspectRatio, thumbnailSize);
237+
// use scaling for thumbnail
238+
if (scaledSize.width > 0 && scaledSize.height > 0 && scaledSize.width != handleWidth && scaledSize.height != handleHeight) {
239+
heif_image *scaled_img;
240+
error = heif_image_scale_image(img, &scaled_img, scaledSize.width, scaledSize.height, NULL);
241+
heif_image_release(img);
242+
if (error.code != heif_error_Ok) {
243+
return nil;
244+
}
245+
img = scaled_img;
246+
}
247+
144248
int width, height, stride, bitsPerPixel, bitsPerComponent;
145-
width = heif_image_get_width(img, heif_channel_interleaved);
249+
width = heif_image_get_width(img, heif_channel_interleaved);
146250
height = heif_image_get_height(img, heif_channel_interleaved);
147251
bitsPerPixel = heif_image_get_bits_per_pixel(img, heif_channel_interleaved);
148252
bitsPerComponent = hasHighDepth ? 16 : 8;
@@ -152,8 +256,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
152256
const uint8_t *rgba = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
153257
if (!rgba) {
154258
heif_image_release(img);
155-
heif_image_handle_release(handle);
156-
heif_context_free(ctx);
157259
return nil;
158260
}
159261
CGDataProviderRef provider =
@@ -165,8 +267,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
165267

166268
// clean up
167269
CGDataProviderRelease(provider);
168-
heif_image_handle_release(handle);
169-
heif_context_free(ctx);
170270

171271
return imageRef;
172272
}

0 commit comments

Comments
 (0)