TwitterImagePipeline/TIPImageFetchProgressiveLoadingPolicies.m (153 lines of code) (raw):
//
// TIPImageFetchProgressiveLoadingPolicies.m
// TwitterImagePipeline
//
// Created on 4/15/15.
// Copyright (c) 2015 Twitter. All rights reserved.
//
#import "TIPImageFetchProgressiveLoadingPolicies.h"
NS_ASSUME_NONNULL_BEGIN
NSDictionary<NSString *, id<TIPImageFetchProgressiveLoadingPolicy>> *TIPImageFetchProgressiveLoadingPolicyDefaultPolicies()
{
static NSDictionary *sDefaultPolicies = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if __LP64__
// fast
sDefaultPolicies = @{
TIPImageTypeJPEG : [[TIPFullFrameProgressiveLoadingPolicy alloc] init]
};
#else
// slow
sDefaultPolicies = @{
TIPImageTypeJPEG : [[TIPFirstAndLastFrameProgressiveLoadingPolicy alloc] init]
};
#endif
});
return sDefaultPolicies;
}
@implementation TIPFirstAndLastFrameProgressiveLoadingPolicy
- (instancetype)init
{
if (self = [super init]) {
_shouldRenderLowQualityFrame = YES;
}
return self;
}
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
if (TIPImageFetchProgressFullFrame == frameProgress) {
// Always load the last frame
if (progress >= 1.0f) {
return TIPImageFetchProgressUpdateBehaviorUpdateWithFullFrameProgress;
}
// Rendered yet?
if (!renderCount) {
if (self.shouldRenderLowQualityFrame || frameCount >= 2) {
// Load the frame
return TIPImageFetchProgressUpdateBehaviorUpdateWithFullFrameProgress;
}
}
}
return TIPImageFetchProgressUpdateBehaviorNone;
}
@end
@implementation TIPFullFrameProgressiveLoadingPolicy
- (instancetype)init
{
if (self = [super init]) {
_shouldRenderLowQualityFrame = YES;
}
return self;
}
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
// We don't want to use any partial frames.
// When I refer to "frame" in the comments below, I'm refering to full frames.
if (TIPImageFetchProgressFullFrame == frameProgress) {
// Ignoring the first frame, load every frame
if (frameCount > 1) {
return TIPImageFetchProgressUpdateBehaviorUpdateWithFullFrameProgress;
}
// For the first frame:
// if it has 20% or more of the data loaded, we can use it
// also, if we are marked for rendering the low quality frame, we can use it
if (frameCount == 1 && (self.shouldRenderLowQualityFrame || progress >= 0.2f)) {
return TIPImageFetchProgressUpdateBehaviorUpdateWithFullFrameProgress;
}
}
return TIPImageFetchProgressUpdateBehaviorNone;
}
@end
@implementation TIPGreedyProgressiveLoadingPolicy
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
return (progress > self.minimumProgress) ?
TIPImageFetchProgressUpdateBehaviorUpdateWithAnyProgress :
TIPImageFetchProgressUpdateBehaviorNone;
}
@end
@implementation TIPFirstAndLastOpportunityProgressiveLoadingPolicy
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
if (progress >= 1.f) {
return TIPImageFetchProgressUpdateBehaviorUpdateWithFullFrameProgress;
}
if (0 == renderCount && progress > self.minimumProgress) {
return TIPImageFetchProgressUpdateBehaviorUpdateWithAnyProgress;
}
return TIPImageFetchProgressUpdateBehaviorNone;
}
@end
@implementation TIPDisabledProgressiveLoadingPolicy
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
return TIPImageFetchProgressUpdateBehaviorNone;
}
@end
@implementation TIPWrapperProgressiveLoadingPolicy
- (instancetype)init
{
return [self initWithProgressiveLoadingPolicy:nil];
}
- (instancetype) initWithProgressiveLoadingPolicy:(nullable id<TIPImageFetchProgressiveLoadingPolicy>)policy
{
if (self = [super init]) {
_wrappedPolicy = policy;
}
return self;
}
- (TIPImageFetchProgressUpdateBehavior)tip_imageFetchOperation:(TIPImageFetchOperation *)op
behaviorForProgress:(TIPImageFetchProgress)frameProgress
frameCount:(NSUInteger)frameCount
progress:(float)progress
type:(NSString *)type
dimensions:(CGSize)dimensions
renderCount:(NSUInteger)renderCount
{
id<TIPImageFetchProgressiveLoadingPolicy> policy = self.wrappedPolicy;
if (policy) {
return [policy tip_imageFetchOperation:op
behaviorForProgress:frameProgress
frameCount:frameCount
progress:progress
type:type
dimensions:dimensions
renderCount:renderCount];
}
return TIPImageFetchProgressUpdateBehaviorNone;
}
@end
NS_ASSUME_NONNULL_END