react-native-fast-image 8.31.2 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/RNFastImage.podspec +6 -4
- package/android/build.gradle +16 -24
- package/android/consumer-rules.pro +4 -15
- package/android/src/main/java/com/dylanvann/fastimage/FastImageAnimatable.java +1 -1
- package/android/src/{glide-4.15 → main}/java/com/dylanvann/fastimage/FastImageAnimated.java +0 -3
- package/android/src/main/java/com/dylanvann/fastimage/FastImageApng.java +139 -24
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +1 -22
- package/android/src/main/java/com/dylanvann/fastimage/FastImageEvent.java +3 -14
- package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +6 -35
- package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +1 -1
- package/android/src/main/java/com/dylanvann/fastimage/FastImageSvg.java +6 -6
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +1 -7
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +1 -1
- package/dist/index.d.ts +10 -9
- package/ios/FastImage/FFFastImageSource.h +1 -2
- package/ios/FastImage/FFFastImageSource.m +28 -74
- package/package.json +2 -6
- package/android/src/glide-older/java/com/dylanvann/fastimage/FastImageAnimated.java +0 -36
- package/android/src/main/java/com/dylanvann/fastimage/FastImageApngRenderer.java +0 -152
- package/ios/FastImage.xcodeproj/project.pbxproj +0 -479
- package/ios/FastImage.xcodeproj/xcshareddata/xcschemes/FastImage-tvOS.xcscheme +0 -80
|
@@ -8,7 +8,6 @@ import android.app.Activity;
|
|
|
8
8
|
import android.content.Context;
|
|
9
9
|
import android.content.ContextWrapper;
|
|
10
10
|
import android.graphics.PorterDuff;
|
|
11
|
-
import android.os.Build;
|
|
12
11
|
|
|
13
12
|
import androidx.annotation.NonNull;
|
|
14
13
|
|
|
@@ -240,12 +239,7 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
|
|
|
240
239
|
}
|
|
241
240
|
|
|
242
241
|
private static boolean isActivityDestroyed(Activity activity) {
|
|
243
|
-
|
|
244
|
-
return activity.isDestroyed() || activity.isFinishing();
|
|
245
|
-
} else {
|
|
246
|
-
return activity.isFinishing() || activity.isChangingConfigurations();
|
|
247
|
-
}
|
|
248
|
-
|
|
242
|
+
return activity.isDestroyed() || activity.isFinishing();
|
|
249
243
|
}
|
|
250
244
|
|
|
251
245
|
// Legacy architecture only; the New Architecture doesn't use shadow nodes.
|
|
@@ -443,7 +443,7 @@ class FastImageViewWithUrl extends AppCompatImageView {
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
if (resource instanceof FastImageAnimatable) {
|
|
446
|
-
// An APNG: each view's is its own (
|
|
446
|
+
// An APNG: each view's is its own (FastImageApng).
|
|
447
447
|
FastImageAnimatable animatable = (FastImageAnimatable) resource;
|
|
448
448
|
animatable.setLoopCount(mLoopCount, false);
|
|
449
449
|
// The target starts it; paused, it stays on its first frame.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
|
|
2
|
+
import { ColorValue, StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
|
|
3
3
|
import { cacheControl, priority, resizeMode } from './constants';
|
|
4
|
-
type ColorValue = NonNullable<ViewStyle['backgroundColor']>;
|
|
5
4
|
/** How the image fits the view: see [`resizeMode`](#resizemode). */
|
|
6
5
|
export type ResizeMode = 'contain' | 'cover' | 'stretch' | 'center' | 'repeat';
|
|
7
6
|
/** An image's load priority: see [`source.priority`](#sourcepriority). */
|
|
@@ -15,9 +14,9 @@ export type Source = {
|
|
|
15
14
|
*
|
|
16
15
|
* Also loads local files (`file://`, and on Android `content://`), images
|
|
17
16
|
* in the app by name (e.g. `'my_image'`: in its asset catalog on iOS, a
|
|
18
|
-
* drawable on Android), photo library images on iOS (`ph://`,
|
|
19
|
-
* [Photo library images](#photo-library-images-ios)) and SVG
|
|
20
|
-
* [SVG images](docs/formats.md#svg-images)).
|
|
17
|
+
* drawable on Android), photo library images on iOS and tvOS (`ph://`,
|
|
18
|
+
* see [Photo library images](#photo-library-images-ios-and-tvos)) and SVG
|
|
19
|
+
* images (see [SVG images](docs/formats.md#svg-images)).
|
|
21
20
|
*/
|
|
22
21
|
uri?: string;
|
|
23
22
|
/** Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`. */
|
|
@@ -221,8 +220,8 @@ export interface OnProgressEvent {
|
|
|
221
220
|
};
|
|
222
221
|
}
|
|
223
222
|
export interface ImageStyle extends ViewStyle {
|
|
224
|
-
overlayColor?:
|
|
225
|
-
tintColor?:
|
|
223
|
+
overlayColor?: ColorValue;
|
|
224
|
+
tintColor?: ColorValue;
|
|
226
225
|
}
|
|
227
226
|
export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
228
227
|
/**
|
|
@@ -493,8 +492,10 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
|
|
|
493
492
|
*/
|
|
494
493
|
tintColor?: ColorValue;
|
|
495
494
|
/**
|
|
496
|
-
* Render children within the image.
|
|
497
|
-
*
|
|
495
|
+
* Render children within the image.
|
|
496
|
+
*
|
|
497
|
+
* @deprecated In the next major version, `FastImage` won't render
|
|
498
|
+
* children: use `FastImageBackground`.
|
|
498
499
|
*/
|
|
499
500
|
children?: React.ReactNode;
|
|
500
501
|
}
|
|
@@ -64,8 +64,7 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
|
|
|
64
64
|
// which is decoded at about the view's size.
|
|
65
65
|
- (BOOL)isPhotoLibrary;
|
|
66
66
|
|
|
67
|
-
// The error's message,
|
|
68
|
-
// the app doesn't have SDWebImageSVGCoder, what to add.
|
|
67
|
+
// The error's message, for onError.
|
|
69
68
|
- (NSString *)errorMessage:(NSError *)error;
|
|
70
69
|
|
|
71
70
|
// A photo library image's own size in pixels (for onLoad when it comes from
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
#import <SDWebImage/SDImageAWebPCoder.h>
|
|
10
10
|
#import "FFFAnimatedWebPCoder.h"
|
|
11
11
|
#import "FFFAnimatedAVIFCoder.h"
|
|
12
|
-
#import <
|
|
12
|
+
#import <SDWebImageSVGCoder/SDImageSVGCoder.h>
|
|
13
|
+
#import <SDWebImagePhotosPlugin/SDImagePhotosLoader.h>
|
|
14
|
+
#import <Photos/Photos.h>
|
|
13
15
|
#import <ImageIO/ImageIO.h>
|
|
14
16
|
|
|
15
17
|
// In FFFastImageView.m.
|
|
@@ -18,24 +20,16 @@ FOUNDATION_EXTERN NSString *FFFErrorMessage(NSError *error);
|
|
|
18
20
|
static NSUInteger const FFFWebCacheSize = 50 * 1024 * 1024;
|
|
19
21
|
|
|
20
22
|
// The photo's own size in pixels, looked up by its identifier (the url after
|
|
21
|
-
// ph://), or zero.
|
|
22
|
-
// with SDWebImagePhotosPlugin (on iOS, always). Kept once looked up.
|
|
23
|
+
// ph://), or zero.
|
|
23
24
|
static CGSize FFFLookUpPhotoPixelSize(NSURL *url)
|
|
24
25
|
{
|
|
25
|
-
Class assetClass = NSClassFromString(@"PHAsset");
|
|
26
|
-
SEL fetch = NSSelectorFromString(@"fetchAssetsWithLocalIdentifiers:options:");
|
|
27
26
|
NSString *prefix = @"ph://";
|
|
28
27
|
NSString *string = url.absoluteString;
|
|
29
|
-
if (!
|
|
28
|
+
if (![string hasPrefix:prefix]) {
|
|
30
29
|
return CGSizeZero;
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
id asset = [result respondsToSelector:@selector(firstObject)] ? [result firstObject] : nil;
|
|
35
|
-
if (!asset) {
|
|
36
|
-
return CGSizeZero;
|
|
37
|
-
}
|
|
38
|
-
return CGSizeMake([[asset valueForKey:@"pixelWidth"] doubleValue], [[asset valueForKey:@"pixelHeight"] doubleValue]);
|
|
31
|
+
PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[[string substringFromIndex:prefix.length]] options:nil].firstObject;
|
|
32
|
+
return asset ? CGSizeMake(asset.pixelWidth, asset.pixelHeight) : CGSizeZero;
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
static NSCache<NSString *, NSValue *> *FFFPhotoSizes(void)
|
|
@@ -132,13 +126,11 @@ static NSString *FFFBundleAssetName(NSURL *url)
|
|
|
132
126
|
@end
|
|
133
127
|
|
|
134
128
|
// Loads photo library images (ph://<localIdentifier>) with
|
|
135
|
-
// SDWebImagePhotosPlugin's loader
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// plugin, or for a source it can't load (assets-library://), the load fails
|
|
139
|
-
// with `failure`.
|
|
129
|
+
// SDWebImagePhotosPlugin's loader, its own instance, so the app's use of the
|
|
130
|
+
// plugin is left as it is. For a source it can't load (assets-library://),
|
|
131
|
+
// the load fails with `failure`.
|
|
140
132
|
@interface FFFPhotosLoader : NSObject <SDImageLoader>
|
|
141
|
-
@property (nonatomic, strong)
|
|
133
|
+
@property (nonatomic, strong) SDImagePhotosLoader *plugin;
|
|
142
134
|
@property (nonatomic, copy) NSString *failure;
|
|
143
135
|
@end
|
|
144
136
|
|
|
@@ -150,24 +142,12 @@ static NSString *FFFBundleAssetName(NSURL *url)
|
|
|
150
142
|
static dispatch_once_t once;
|
|
151
143
|
dispatch_once(&once, ^{
|
|
152
144
|
loader = [FFFPhotosLoader new];
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// PHImageRequestOptionsDeliveryModeHighQualityFormat. Skipped if
|
|
160
|
-
// a later version of the plugin renames these.
|
|
161
|
-
NSObject *requestOptions = [(NSObject *)plugin respondsToSelector:NSSelectorFromString(@"imageRequestOptions")]
|
|
162
|
-
? [(NSObject *)plugin valueForKey:@"imageRequestOptions"]
|
|
163
|
-
: nil;
|
|
164
|
-
if ([requestOptions respondsToSelector:NSSelectorFromString(@"setDeliveryMode:")]) {
|
|
165
|
-
[requestOptions setValue:@1 forKey:@"deliveryMode"];
|
|
166
|
-
}
|
|
167
|
-
loader.plugin = plugin;
|
|
168
|
-
} else {
|
|
169
|
-
loader.failure = @"Photo library images (ph://) need SDWebImagePhotosPlugin: add pod 'SDWebImagePhotosPlugin' to the app's Podfile";
|
|
170
|
-
}
|
|
145
|
+
SDImagePhotosLoader *plugin = [SDImagePhotosLoader new];
|
|
146
|
+
// One image per load, at full quality. By default the plugin first
|
|
147
|
+
// sends a quick low quality one, which a view would show (and fade
|
|
148
|
+
// in) before the one it asked for.
|
|
149
|
+
plugin.imageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
|
|
150
|
+
loader.plugin = plugin;
|
|
171
151
|
});
|
|
172
152
|
return loader;
|
|
173
153
|
}
|
|
@@ -304,10 +284,6 @@ static BOOL FFFLooksLikeSVG(NSData *data)
|
|
|
304
284
|
|
|
305
285
|
@end
|
|
306
286
|
|
|
307
|
-
// SDWebImageSVGCoder's coder, when the app has the pod (SVG images): found at
|
|
308
|
-
// runtime, so FastImage doesn't depend on it. Nil otherwise.
|
|
309
|
-
static id<SDImageCoder> FFFSVGCoder;
|
|
310
|
-
|
|
311
287
|
@implementation FFFastImageSource
|
|
312
288
|
|
|
313
289
|
+ (void)initialize
|
|
@@ -345,28 +321,17 @@ static id<SDImageCoder> FFFSVGCoder;
|
|
|
345
321
|
[SDImageCodersManager.sharedManager addCoder:FFFAnimatedAVIFCoder.sharedCoder];
|
|
346
322
|
}
|
|
347
323
|
|
|
348
|
-
// SVG: registered with SDWebImage's coders, as the
|
|
349
|
-
// the app has already): SDWebImage then picks it
|
|
350
|
-
// for FastImage's animated and downsampled image
|
|
351
|
-
// coder per load instead (SDWebImageContextImageCoder)
|
|
352
|
-
// only coder for everything in that load.
|
|
353
|
-
// use can decode SVG too, which is what the pod is for.
|
|
354
|
-
Class coderClass = NSClassFromString(@"SDImageSVGCoder");
|
|
355
|
-
SEL shared = NSSelectorFromString(@"sharedCoder");
|
|
356
|
-
if (!coderClass || ![coderClass respondsToSelector:shared]) {
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
id coder = ((id (*)(id, SEL))objc_msgSend)(coderClass, shared);
|
|
360
|
-
if (![coder conformsToProtocol:@protocol(SDImageCoder)]) {
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
FFFSVGCoder = coder;
|
|
324
|
+
// SVG (SDWebImageSVGCoder): registered with SDWebImage's coders, as the
|
|
325
|
+
// pod's setup does (unless the app has already): SDWebImage then picks it
|
|
326
|
+
// for SVG data, including for FastImage's animated and downsampled image
|
|
327
|
+
// classes. Giving the coder per load instead (SDWebImageContextImageCoder)
|
|
328
|
+
// would make it the only coder for everything in that load.
|
|
364
329
|
for (id<SDImageCoder> registered in SDImageCodersManager.sharedManager.coders) {
|
|
365
|
-
if ([registered isKindOfClass:
|
|
330
|
+
if ([registered isKindOfClass:[SDImageSVGCoder class]]) {
|
|
366
331
|
return;
|
|
367
332
|
}
|
|
368
333
|
}
|
|
369
|
-
[SDImageCodersManager.sharedManager addCoder:
|
|
334
|
+
[SDImageCodersManager.sharedManager addCoder:SDImageSVGCoder.sharedCoder];
|
|
370
335
|
}
|
|
371
336
|
|
|
372
337
|
- (instancetype)initWithURL:(NSURL *)url
|
|
@@ -412,10 +377,6 @@ static id<SDImageCoder> FFFSVGCoder;
|
|
|
412
377
|
|
|
413
378
|
- (NSString *)errorMessage:(NSError *)error
|
|
414
379
|
{
|
|
415
|
-
if (!FFFSVGCoder && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorBadImageData &&
|
|
416
|
-
[_url.path.lowercaseString hasSuffix:@".svg"]) {
|
|
417
|
-
return @"SVG images need SDWebImageSVGCoder: add pod 'SDWebImageSVGCoder' to the app's Podfile";
|
|
418
|
-
}
|
|
419
380
|
return FFFErrorMessage(error);
|
|
420
381
|
}
|
|
421
382
|
|
|
@@ -508,17 +469,10 @@ static id<SDImageCoder> FFFSVGCoder;
|
|
|
508
469
|
static dispatch_once_t once;
|
|
509
470
|
dispatch_once(&once, ^{
|
|
510
471
|
NSString *name = @"fast-image-http-cache";
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
directoryURL:[caches URLByAppendingPathComponent:name]];
|
|
516
|
-
} else {
|
|
517
|
-
// In the app's caches directory.
|
|
518
|
-
cache = [[NSURLCache alloc] initWithMemoryCapacity:0
|
|
519
|
-
diskCapacity:FFFWebCacheSize
|
|
520
|
-
diskPath:name];
|
|
521
|
-
}
|
|
472
|
+
NSURL *caches = [NSFileManager.defaultManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask].firstObject;
|
|
473
|
+
cache = [[NSURLCache alloc] initWithMemoryCapacity:0
|
|
474
|
+
diskCapacity:FFFWebCacheSize
|
|
475
|
+
directoryURL:[caches URLByAppendingPathComponent:name]];
|
|
522
476
|
});
|
|
523
477
|
return cache;
|
|
524
478
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -28,11 +28,7 @@
|
|
|
28
28
|
"android/build.gradle",
|
|
29
29
|
"android/consumer-rules.pro",
|
|
30
30
|
"android/src/main",
|
|
31
|
-
"android/src/glide-4.15",
|
|
32
|
-
"android/src/glide-older",
|
|
33
31
|
"ios/FastImage",
|
|
34
|
-
"ios/FastImage.xcodeproj/project.pbxproj",
|
|
35
|
-
"ios/FastImage.xcodeproj/xcshareddata",
|
|
36
32
|
"app.plugin.js",
|
|
37
33
|
"dist",
|
|
38
34
|
"RNFastImage.podspec"
|
|
@@ -73,6 +69,6 @@
|
|
|
73
69
|
},
|
|
74
70
|
"peerDependencies": {
|
|
75
71
|
"react": "^17 || ^18 || ^19",
|
|
76
|
-
"react-native": ">=0.
|
|
72
|
+
"react-native": ">=0.65.0"
|
|
77
73
|
}
|
|
78
74
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
package com.dylanvann.fastimage;
|
|
2
|
-
|
|
3
|
-
import android.content.Context;
|
|
4
|
-
import android.graphics.drawable.Drawable;
|
|
5
|
-
|
|
6
|
-
import androidx.annotation.NonNull;
|
|
7
|
-
import androidx.annotation.Nullable;
|
|
8
|
-
|
|
9
|
-
import com.bumptech.glide.Glide;
|
|
10
|
-
import com.bumptech.glide.Registry;
|
|
11
|
-
|
|
12
|
-
// Built instead of src/glide-4.15's FastImageAnimated with a Glide before
|
|
13
|
-
// 4.15 (build.gradle), which has no decoder for animated WebP or AVIF: they
|
|
14
|
-
// show their first frame, so there's nothing to register, remember or copy.
|
|
15
|
-
final class FastImageAnimated {
|
|
16
|
-
private FastImageAnimated() {}
|
|
17
|
-
|
|
18
|
-
interface CopyCallback {
|
|
19
|
-
void onCopy(@Nullable Drawable copy);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
static void register(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {}
|
|
23
|
-
|
|
24
|
-
@Nullable
|
|
25
|
-
static Integer repeatCount(@NonNull Drawable drawable) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static boolean shownElsewhere(@NonNull Drawable drawable, @NonNull Drawable.Callback view) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static void copy(@NonNull Drawable drawable, @NonNull CopyCallback callback) {
|
|
34
|
-
callback.onCopy(null);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
package com.dylanvann.fastimage;
|
|
2
|
-
|
|
3
|
-
import android.graphics.Rect;
|
|
4
|
-
import android.graphics.drawable.Drawable;
|
|
5
|
-
|
|
6
|
-
import androidx.annotation.NonNull;
|
|
7
|
-
import androidx.annotation.Nullable;
|
|
8
|
-
|
|
9
|
-
import com.bumptech.glide.load.engine.Resource;
|
|
10
|
-
import com.github.penfeizhou.animation.apng.APNGDrawable;
|
|
11
|
-
import com.github.penfeizhou.animation.apng.decode.APNGDecoder;
|
|
12
|
-
import com.github.penfeizhou.animation.loader.ByteBufferLoader;
|
|
13
|
-
import com.github.penfeizhou.animation.loader.Loader;
|
|
14
|
-
|
|
15
|
-
import java.nio.ByteBuffer;
|
|
16
|
-
|
|
17
|
-
// APNGs with APNG4Android (FastImageApng, which only uses this when it's
|
|
18
|
-
// there). Glide keeps the image's data in its memory cache, and each view
|
|
19
|
-
// gets a drawable of its own over it, so views don't share playback: making
|
|
20
|
-
// one only reads the image's header, and it decodes its frames (at a sample
|
|
21
|
-
// size from the size it's drawn at) on APNG4Android's threads as it plays.
|
|
22
|
-
final class FastImageApngRenderer {
|
|
23
|
-
private FastImageApngRenderer() {
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Throws NoClassDefFoundError without APNG4Android.
|
|
27
|
-
static boolean available() {
|
|
28
|
-
return APNGDrawable.class.getName() != null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private static Loader loader(ByteBuffer data) {
|
|
32
|
-
return new ByteBufferLoader() {
|
|
33
|
-
@Override
|
|
34
|
-
public ByteBuffer getByteBuffer() {
|
|
35
|
-
ByteBuffer buffer = data.duplicate();
|
|
36
|
-
buffer.position(0);
|
|
37
|
-
return buffer;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Null if APNG4Android can't read it (Glide then decodes it as a PNG).
|
|
43
|
-
@Nullable
|
|
44
|
-
static Resource<Drawable> decode(@NonNull ByteBuffer data, int plays) {
|
|
45
|
-
// Reads the header, waiting for APNG4Android's thread.
|
|
46
|
-
Rect bounds = new APNGDecoder(loader(data), null).getBounds();
|
|
47
|
-
if (bounds.isEmpty()) return null;
|
|
48
|
-
return new ApngResource(data, plays, bounds);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private static final class ApngResource implements Resource<Drawable> {
|
|
52
|
-
private final ByteBuffer data;
|
|
53
|
-
private final int plays;
|
|
54
|
-
private final Rect bounds;
|
|
55
|
-
|
|
56
|
-
ApngResource(ByteBuffer data, int plays, Rect bounds) {
|
|
57
|
-
this.data = data;
|
|
58
|
-
this.plays = plays;
|
|
59
|
-
this.bounds = bounds;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@NonNull
|
|
63
|
-
@Override
|
|
64
|
-
public Class<Drawable> getResourceClass() {
|
|
65
|
-
return Drawable.class;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@NonNull
|
|
69
|
-
@Override
|
|
70
|
-
public Drawable get() {
|
|
71
|
-
return new ApngDrawable(loader(data), plays);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// The data, and a frame at full size (each view's drawable has its
|
|
75
|
-
// own, at the size it's drawn at).
|
|
76
|
-
@Override
|
|
77
|
-
public int getSize() {
|
|
78
|
-
return data.limit() + bounds.width() * bounds.height() * 4;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@Override
|
|
82
|
-
public void recycle() {
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private static final class ApngDrawable extends APNGDrawable implements FastImageAnimatable {
|
|
87
|
-
// How many times the file says it plays (0: forever).
|
|
88
|
-
private final int plays;
|
|
89
|
-
private volatile boolean paused = false;
|
|
90
|
-
// Once a frame has been rendered (on the decoder's thread).
|
|
91
|
-
private volatile boolean rendered = false;
|
|
92
|
-
|
|
93
|
-
ApngDrawable(Loader loader, int plays) {
|
|
94
|
-
super(loader);
|
|
95
|
-
this.plays = plays;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@Override
|
|
99
|
-
public void setLoopCount(int loopCount, boolean restart) {
|
|
100
|
-
// Plays (0: forever), as APNG4Android counts them.
|
|
101
|
-
setLoopLimit(loopCount == -1 ? plays : loopCount);
|
|
102
|
-
if (restart) {
|
|
103
|
-
reset();
|
|
104
|
-
start();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@Override
|
|
109
|
-
public void setPaused(boolean paused) {
|
|
110
|
-
this.paused = paused;
|
|
111
|
-
// It starts and stops itself as it's shown and hidden, but not
|
|
112
|
-
// while paused.
|
|
113
|
-
setAutoPlay(!paused);
|
|
114
|
-
if (paused) {
|
|
115
|
-
// Before the first frame, onStart pauses once it's rendered:
|
|
116
|
-
// the decoder's start clears a pause, then skips rendering if
|
|
117
|
-
// one came in meanwhile, which left the view blank.
|
|
118
|
-
if (rendered) pause();
|
|
119
|
-
} else {
|
|
120
|
-
resume();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// From its first frame. The target starts it right after
|
|
125
|
-
// setVisible(true) has (auto play): APNG4Android's start() stops and
|
|
126
|
-
// starts a running decoder, and a stop made while the decoder is
|
|
127
|
-
// still starting can win over that start, leaving it stopped on its
|
|
128
|
-
// first frame. A running one is reset to its first frame instead.
|
|
129
|
-
@Override
|
|
130
|
-
public void start() {
|
|
131
|
-
if (isRunning()) {
|
|
132
|
-
getFrameSeqDecoder().reset();
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
super.start();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
@Override
|
|
139
|
-
public void onRender(ByteBuffer byteBuffer) {
|
|
140
|
-
super.onRender(byteBuffer);
|
|
141
|
-
rendered = true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// On its decoder thread, once starting has drawn the first frame:
|
|
145
|
-
// starting clears a pause made before it got there, so pause again.
|
|
146
|
-
@Override
|
|
147
|
-
public void onStart() {
|
|
148
|
-
super.onStart();
|
|
149
|
-
if (paused) getFrameSeqDecoder().pause();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|