@@ -43,6 +43,38 @@ static void FreeImageData(void *info, const void *data, size_t size) {
43
43
heif_image_release (img); // `heif_image_release` will free the bitmap buffer. We do not call `free`
44
44
}
45
45
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
+
46
78
@implementation SDImageHEIFCoder
47
79
48
80
+ (instancetype )sharedCoder {
@@ -74,8 +106,24 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
74
106
}
75
107
}
76
108
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
+
77
125
// Currently only support primary image :)
78
- CGImageRef imageRef = [self sd_createHEIFImageWithData: data];
126
+ CGImageRef imageRef = [self sd_createHEIFImageWithData: data thumbnailSize: thumbnailSize preserveAspectRatio: preserveAspectRatio ];
79
127
if (!imageRef) {
80
128
return nil ;
81
129
}
@@ -91,7 +139,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
91
139
}
92
140
93
141
// 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 {
95
143
heif_context* ctx = heif_context_alloc ();
96
144
if (!ctx) {
97
145
return nil ;
@@ -113,6 +161,50 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
113
161
return nil ;
114
162
}
115
163
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
+
116
208
// check alpha channel
117
209
BOOL hasAlpha = heif_image_handle_has_alpha_channel (handle);
118
210
int depth = heif_image_handle_get_chroma_bits_per_pixel (handle);
@@ -136,13 +228,25 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
136
228
heif_image* img;
137
229
error = heif_decode_image (handle, &img, heif_colorspace_RGB, chroma, NULL );
138
230
if (error.code != heif_error_Ok) {
139
- heif_image_handle_release (handle);
140
- heif_context_free (ctx);
141
231
return nil ;
142
232
}
143
233
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
+
144
248
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);
146
250
height = heif_image_get_height (img, heif_channel_interleaved);
147
251
bitsPerPixel = heif_image_get_bits_per_pixel (img, heif_channel_interleaved);
148
252
bitsPerComponent = hasHighDepth ? 16 : 8 ;
@@ -152,8 +256,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
152
256
const uint8_t *rgba = heif_image_get_plane_readonly (img, heif_channel_interleaved, &stride);
153
257
if (!rgba) {
154
258
heif_image_release (img);
155
- heif_image_handle_release (handle);
156
- heif_context_free (ctx);
157
259
return nil ;
158
260
}
159
261
CGDataProviderRef provider =
@@ -165,8 +267,6 @@ - (nullable CGImageRef)sd_createHEIFImageWithData:(nonnull NSData *)data CF_RETU
165
267
166
268
// clean up
167
269
CGDataProviderRelease (provider);
168
- heif_image_handle_release (handle);
169
- heif_context_free (ctx);
170
270
171
271
return imageRef;
172
272
}
0 commit comments