Initial checkin of the project - minimal code + docs + Travis CI script

This commit is contained in:
Bogdan Poplauschi 2018-08-28 18:21:20 +03:00
commit 091a6fd658
27 changed files with 1904 additions and 0 deletions

67
.gitignore vendored Normal file
View File

@ -0,0 +1,67 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
Podfile.lock
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/

38
.travis.yml Normal file
View File

@ -0,0 +1,38 @@
language: objective-c
osx_image: xcode9
env:
global:
- LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8
addons:
ssh_known_hosts: github.com
notifications:
email: false
before_install:
- env
- locale
- gem install cocoapods --no-rdoc --no-ri --no-document --quiet
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
- pod --version
- pod setup --silent > /dev/null
- pod repo update --silent
- xcpretty --version
- xcodebuild -version
- xcodebuild -showsdks
script:
- set -o pipefail
- echo Check if the library described by the podspec can be built
- pod lib lint --allow-warnings
- echo Build example
- pod install --project-directory=Example
- xcodebuild build -workspace Example/SDImageWebPCoderExample.xcworkspace -scheme SDImageWebPCoderExample -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' -configuration Debug | xcpretty -c
# - echo Run the tests
# - xcodebuild test -workspace Example/SDImageWebPCoderExample.xcworkspace -scheme 'SDImageWebPCoderTests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' -configuration Debug | xcpretty -c

2
Cartfile Normal file
View File

@ -0,0 +1,2 @@
github "rs/SDWebImage" ~> 5.0.0-beta
github "SDWebImage/libwebp-Xcode" >= 1.0.0

2
Cartfile.resolved Normal file
View File

@ -0,0 +1,2 @@
github "SDWebImage/libwebp-Xcode" "1.0.0"
github "rs/SDWebImage" "5.0.0-beta2"

6
Example/Podfile Normal file
View File

@ -0,0 +1,6 @@
platform :ios, '8.0'
use_frameworks!
target 'SDImageWebPCoderExample' do
pod 'SDImageWebPCoder', :path => '../'
end

View File

@ -0,0 +1,403 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
0358F0904AE0B377A509E317 /* Pods_SDImageWebPCoderExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08D062B84105453A729B2E18 /* Pods_SDImageWebPCoderExample.framework */; };
803D79D3213597CB00C815FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 803D79D2213597CB00C815FC /* AppDelegate.m */; };
803D79D6213597CB00C815FC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 803D79D5213597CB00C815FC /* ViewController.m */; };
803D79D9213597CB00C815FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 803D79D7213597CB00C815FC /* Main.storyboard */; };
803D79DB213597CC00C815FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 803D79DA213597CC00C815FC /* Assets.xcassets */; };
803D79DE213597CC00C815FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 803D79DC213597CC00C815FC /* LaunchScreen.storyboard */; };
803D79E1213597CC00C815FC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 803D79E0213597CC00C815FC /* main.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
08D062B84105453A729B2E18 /* Pods_SDImageWebPCoderExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDImageWebPCoderExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6B2A8799CA055DEFD6287E90 /* Pods-SDImageWebPCoderExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDImageWebPCoderExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-SDImageWebPCoderExample/Pods-SDImageWebPCoderExample.release.xcconfig"; sourceTree = "<group>"; };
803D79CE213597CB00C815FC /* SDImageWebPCoderExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SDImageWebPCoderExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
803D79D1213597CB00C815FC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
803D79D2213597CB00C815FC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
803D79D4213597CB00C815FC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
803D79D5213597CB00C815FC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
803D79D8213597CB00C815FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
803D79DA213597CC00C815FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
803D79DD213597CC00C815FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
803D79DF213597CC00C815FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
803D79E0213597CC00C815FC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
9C8C6854F3571B183FE25D4E /* Pods-SDImageWebPCoderExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDImageWebPCoderExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SDImageWebPCoderExample/Pods-SDImageWebPCoderExample.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
803D79CB213597CB00C815FC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0358F0904AE0B377A509E317 /* Pods_SDImageWebPCoderExample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
09A0AFCF80F579FDF7C105AC /* Pods */ = {
isa = PBXGroup;
children = (
9C8C6854F3571B183FE25D4E /* Pods-SDImageWebPCoderExample.debug.xcconfig */,
6B2A8799CA055DEFD6287E90 /* Pods-SDImageWebPCoderExample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
4219260BABAB0FF55F41B025 /* Frameworks */ = {
isa = PBXGroup;
children = (
08D062B84105453A729B2E18 /* Pods_SDImageWebPCoderExample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
803D79C5213597CB00C815FC = {
isa = PBXGroup;
children = (
803D79D0213597CB00C815FC /* SDImageWebPCoderExample */,
803D79CF213597CB00C815FC /* Products */,
09A0AFCF80F579FDF7C105AC /* Pods */,
4219260BABAB0FF55F41B025 /* Frameworks */,
);
sourceTree = "<group>";
};
803D79CF213597CB00C815FC /* Products */ = {
isa = PBXGroup;
children = (
803D79CE213597CB00C815FC /* SDImageWebPCoderExample.app */,
);
name = Products;
sourceTree = "<group>";
};
803D79D0213597CB00C815FC /* SDImageWebPCoderExample */ = {
isa = PBXGroup;
children = (
803D79D1213597CB00C815FC /* AppDelegate.h */,
803D79D2213597CB00C815FC /* AppDelegate.m */,
803D79D4213597CB00C815FC /* ViewController.h */,
803D79D5213597CB00C815FC /* ViewController.m */,
803D79D7213597CB00C815FC /* Main.storyboard */,
803D79DA213597CC00C815FC /* Assets.xcassets */,
803D79DC213597CC00C815FC /* LaunchScreen.storyboard */,
803D79DF213597CC00C815FC /* Info.plist */,
803D79E0213597CC00C815FC /* main.m */,
);
path = SDImageWebPCoderExample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
803D79CD213597CB00C815FC /* SDImageWebPCoderExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 803D79E4213597CC00C815FC /* Build configuration list for PBXNativeTarget "SDImageWebPCoderExample" */;
buildPhases = (
47B6AB88C962A96D10FA0EF4 /* [CP] Check Pods Manifest.lock */,
803D79CA213597CB00C815FC /* Sources */,
803D79CB213597CB00C815FC /* Frameworks */,
803D79CC213597CB00C815FC /* Resources */,
3C8DAE2B7C7EB19EB08A4E7D /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = SDImageWebPCoderExample;
productName = SDImageWebPCoderExample;
productReference = 803D79CE213597CB00C815FC /* SDImageWebPCoderExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
803D79C6213597CB00C815FC /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0940;
ORGANIZATIONNAME = SDWebImage;
TargetAttributes = {
803D79CD213597CB00C815FC = {
CreatedOnToolsVersion = 9.4.1;
};
};
};
buildConfigurationList = 803D79C9213597CB00C815FC /* Build configuration list for PBXProject "SDImageWebPCoderExample" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 803D79C5213597CB00C815FC;
productRefGroup = 803D79CF213597CB00C815FC /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
803D79CD213597CB00C815FC /* SDImageWebPCoderExample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
803D79CC213597CB00C815FC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
803D79DE213597CC00C815FC /* LaunchScreen.storyboard in Resources */,
803D79DB213597CC00C815FC /* Assets.xcassets in Resources */,
803D79D9213597CB00C815FC /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3C8DAE2B7C7EB19EB08A4E7D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-SDImageWebPCoderExample/Pods-SDImageWebPCoderExample-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/SDImageWebPCoder/SDImageWebPCoder.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDImageWebPCoder.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SDImageWebPCoderExample/Pods-SDImageWebPCoderExample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
47B6AB88C962A96D10FA0EF4 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-SDImageWebPCoderExample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
803D79CA213597CB00C815FC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
803D79D6213597CB00C815FC /* ViewController.m in Sources */,
803D79E1213597CC00C815FC /* main.m in Sources */,
803D79D3213597CB00C815FC /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
803D79D7213597CB00C815FC /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
803D79D8213597CB00C815FC /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
803D79DC213597CC00C815FC /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
803D79DD213597CC00C815FC /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
803D79E2213597CC00C815FC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
803D79E3213597CC00C815FC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
803D79E5213597CC00C815FC /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9C8C6854F3571B183FE25D4E /* Pods-SDImageWebPCoderExample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = SDImageWebPCoderExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.SDWebImage.SDImageWebPCoderExample;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
803D79E6213597CC00C815FC /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6B2A8799CA055DEFD6287E90 /* Pods-SDImageWebPCoderExample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = SDImageWebPCoderExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.SDWebImage.SDImageWebPCoderExample;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
803D79C9213597CB00C815FC /* Build configuration list for PBXProject "SDImageWebPCoderExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
803D79E2213597CC00C815FC /* Debug */,
803D79E3213597CC00C815FC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
803D79E4213597CC00C815FC /* Build configuration list for PBXNativeTarget "SDImageWebPCoderExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
803D79E5213597CC00C815FC /* Debug */,
803D79E6213597CC00C815FC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 803D79C6213597CB00C815FC /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:SDImageWebPCoderExample.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:SDImageWebPCoderExample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,17 @@
//
// AppDelegate.h
// SDImageWebPCoderExample
//
// Created by Bogdan Poplauschi on 28/08/2018.
// Copyright © 2018 SDWebImage. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -0,0 +1,51 @@
//
// AppDelegate.m
// SDImageWebPCoderExample
//
// Created by Bogdan Poplauschi on 28/08/2018.
// Copyright © 2018 SDWebImage. All rights reserved.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="RFu-Oe-4ia">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="RFu-Oe-4ia" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="Sdh-Zp-74C"/>
<constraint firstItem="RFu-Oe-4ia" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="U5c-8s-Ehx"/>
<constraint firstItem="RFu-Oe-4ia" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="eH6-kl-Kl9"/>
<constraint firstItem="RFu-Oe-4ia" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="l7U-la-kh4"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="imageView" destination="RFu-Oe-4ia" id="IV9-xD-bYt"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="117.59999999999999" y="118.29085457271366"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,15 @@
//
// ViewController.h
// SDImageWebPCoderExample
//
// Created by Bogdan Poplauschi on 28/08/2018.
// Copyright © 2018 SDWebImage. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

View File

@ -0,0 +1,36 @@
//
// ViewController.m
// SDImageWebPCoderExample
//
// Created by Bogdan Poplauschi on 28/08/2018.
// Copyright © 2018 SDWebImage. All rights reserved.
//
#import "ViewController.h"
#import <SDImageWebPCoder/SDImageWebPCoder.h>
#import <SDWebImage/SDWebImage.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[SDImageCodersManager sharedManager] addCoder:[SDImageWebPCoder sharedCoder]];
[self.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://littlesvr.ca/apng/images/SteamEngine.webp"]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

View File

@ -0,0 +1,16 @@
//
// main.m
// SDImageWebPCoderExample
//
// Created by Bogdan Poplauschi on 28/08/2018.
// Copyright © 2018 SDWebImage. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2018 Bogdan Poplauschi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

59
README.md Normal file
View File

@ -0,0 +1,59 @@
# SDImageWebPCoder
[![CI Status](http://img.shields.io/travis/SDWebImage/SDImageWebPCoder.svg?style=flat)](https://travis-ci.org/SDWebImage/SDImageWebPCoder)
[![Version](https://img.shields.io/cocoapods/v/SDImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDImageWebPCoder)
[![License](https://img.shields.io/cocoapods/l/SDImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDImageWebPCoder)
[![Platform](https://img.shields.io/cocoapods/p/SDImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDImageWebPCoder)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/SDWebImage/SDImageWebPCoder)
Starting with the SDWebImage 5.0 version, we moved the [libwebp](https://github.com/webmproject/libwebp) support code from the Core Repo to this stand-alone repo.
## Requirements
+ iOS 8
+ macOS 10.10
+ tvOS 9.0
+ watchOS 2.0
## Installation
#### CocoaPods
SDImageWebPCoder is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile:
```ruby
pod 'SDImageWebPCoder'
```
#### Carthage
SDImageWebPCoder is available through [Carthage](https://github.com/Carthage/Carthage).
```
github "SDWebImage/SDImageWebPCoder"
```
## Usage
```objective-c
SDImageWebPCoder *webPCoder = [SDImageWebPCoder sharedCoder];
[[SDWebImageCodersManager sharedInstance] addCoder:webPCoder];
UIImageView *imageView;
NSURL *WebPURL = ...;
[imageView sd_setImageWithURL:WebPURL];
```
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
This is a demo to show how to use `WebP` and animated `WebP` images via `SDWebImage`.
## Author
[Bogdan Poplauschi](https://github.com/bpoplauschi)
## License
SDImageWebPCoder is available under the MIT license. See [the LICENSE file](https://github.com/SDWebImage/SDImageWebPCoder/blob/master/LICENSE) for more info.

32
SDImageWebPCoder.podspec Normal file
View File

@ -0,0 +1,32 @@
Pod::Spec.new do |s|
s.name = 'SDImageWebPCoder'
s.version = '0.1.0'
s.summary = 'WebP decoder/encoder for SDWebImage coder plugin.'
s.description = <<-DESC
This is a simple SDWebImage coder plugin to support WebP image.
DESC
s.homepage = 'https://github.com/SDWebImage/SDImageWebPCoder'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Bogdan Poplauschi' => 'bpoplauschi@gmail.com' }
s.source = { :git => 'https://github.com/SDWebImage/SDImageWebPCoder.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0'
s.watchos.deployment_target = '2.0'
s.source_files = 'SDImageWebPCoder/Classes/**/*'
s.xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SD_WEBP=1',
'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src'
}
s.watchos.xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SD_WEBP=1 WEBP_USE_INTRINSICS=1',
'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src'
}
s.dependency 'SDWebImage/Core', '~> 5.0.0-beta'
s.dependency 'libwebp', '~> 1.0'
end

View File

View File

View File

@ -0,0 +1,23 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifdef SD_WEBP
#import <Foundation/Foundation.h>
#import <SDWebImage/SDImageCoder.h>
/**
Built in coder that supports WebP and animated WebP
*/
@interface SDImageWebPCoder : NSObject <SDProgressiveImageCoder, SDAnimatedImageCoder>
@property (nonatomic, class, readonly, nonnull) SDImageWebPCoder *sharedCoder;
@end
#endif

View File

@ -0,0 +1,827 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifdef SD_WEBP
#import "SDImageWebPCoder.h"
#import "SDImageCoderHelper.h"
#import "NSImage+Compatibility.h"
#import "UIImage+Metadata.h"
#import "UIImage+ForceDecode.h"
#if __has_include(<webp/decode.h>) && __has_include(<webp/encode.h>) && __has_include(<webp/demux.h>) && __has_include(<webp/mux.h>)
#import <webp/decode.h>
#import <webp/encode.h>
#import <webp/demux.h>
#import <webp/mux.h>
#else
#import "webp/decode.h"
#import "webp/encode.h"
#import "webp/demux.h"
#import "webp/mux.h"
#endif
#import <Accelerate/Accelerate.h>
@interface SDWebPCoderFrame : NSObject
@property (nonatomic, assign) NSUInteger index; // Frame index (zero based)
@property (nonatomic, assign) NSTimeInterval duration; // Frame duration in seconds
@property (nonatomic, assign) NSUInteger width; // Frame width
@property (nonatomic, assign) NSUInteger height; // Frame height
@property (nonatomic, assign) NSUInteger offsetX; // Frame origin.x in canvas (left-bottom based)
@property (nonatomic, assign) NSUInteger offsetY; // Frame origin.y in canvas (left-bottom based)
@property (nonatomic, assign) BOOL hasAlpha; // Whether frame contains alpha
@property (nonatomic, assign) BOOL isFullSize; // Whether frame size is equal to canvas size
@property (nonatomic, assign) WebPMuxAnimBlend blend; // Frame dispose method
@property (nonatomic, assign) WebPMuxAnimDispose dispose; // Frame blend operation
@property (nonatomic, assign) NSUInteger blendFromIndex; // The nearest previous frame index which blend mode is WEBP_MUX_BLEND
@end
@implementation SDWebPCoderFrame
@end
@implementation SDImageWebPCoder {
WebPIDecoder *_idec;
WebPDemuxer *_demux;
NSData *_imageData;
CGFloat _scale;
NSUInteger _loopCount;
NSUInteger _frameCount;
NSArray<SDWebPCoderFrame *> *_frames;
CGContextRef _canvas;
BOOL _hasAnimation;
BOOL _hasAlpha;
BOOL _finished;
CGFloat _canvasWidth;
CGFloat _canvasHeight;
dispatch_semaphore_t _lock;
NSUInteger _currentBlendIndex;
}
- (void)dealloc {
if (_idec) {
WebPIDelete(_idec);
_idec = NULL;
}
if (_demux) {
WebPDemuxDelete(_demux);
_demux = NULL;
}
if (_canvas) {
CGContextRelease(_canvas);
_canvas = NULL;
}
}
+ (instancetype)sharedCoder {
static SDImageWebPCoder *coder;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coder = [[SDImageWebPCoder alloc] init];
});
return coder;
}
#pragma mark - Decode
- (BOOL)canDecodeFromData:(nullable NSData *)data {
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatWebP);
}
- (BOOL)canIncrementalDecodeFromData:(NSData *)data {
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatWebP);
}
- (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderOptions *)options {
if (!data) {
return nil;
}
WebPData webpData;
WebPDataInit(&webpData);
webpData.bytes = data.bytes;
webpData.size = data.length;
WebPDemuxer *demuxer = WebPDemux(&webpData);
if (!demuxer) {
return nil;
}
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
BOOL hasAnimation = flags & ANIMATION_FLAG;
BOOL decodeFirstFrame = [options[SDImageCoderDecodeFirstFrameOnly] boolValue];
CGFloat scale = 1;
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
if (scaleFactor != nil) {
scale = [scaleFactor doubleValue];
if (scale < 1) {
scale = 1;
}
}
if (!hasAnimation) {
// for static single webp image
CGImageRef imageRef = [self sd_createWebpImageWithData:webpData];
if (!imageRef) {
return nil;
}
#if SD_UIKIT || SD_WATCH
UIImage *staticImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
#else
UIImage *staticImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:kCGImagePropertyOrientationUp];
#endif
CGImageRelease(imageRef);
WebPDemuxDelete(demuxer);
return staticImage;
}
// for animated webp image
WebPIterator iter;
// libwebp's index start with 1
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
WebPDemuxReleaseIterator(&iter);
WebPDemuxDelete(demuxer);
return nil;
}
if (decodeFirstFrame) {
// first frame for animated webp image
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment];
#if SD_UIKIT || SD_WATCH
UIImage *firstFrameImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
#else
UIImage *firstFrameImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:kCGImagePropertyOrientationUp];
#endif
firstFrameImage.sd_imageFormat = SDImageFormatWebP;
CGImageRelease(imageRef);
WebPDemuxReleaseIterator(&iter);
WebPDemuxDelete(demuxer);
return firstFrameImage;
}
int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
BOOL hasAlpha = flags & ALPHA_FLAG;
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
CGContextRef canvas = CGBitmapContextCreate(NULL, canvasWidth, canvasHeight, 8, 0, [SDImageCoderHelper colorSpaceGetDeviceRGB], bitmapInfo);
if (!canvas) {
WebPDemuxDelete(demuxer);
return nil;
}
NSMutableArray<SDImageFrame *> *frames = [NSMutableArray array];
do {
@autoreleasepool {
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:canvas iterator:iter];
if (!imageRef) {
continue;
}
#if SD_UIKIT || SD_WATCH
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
#else
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:kCGImagePropertyOrientationUp];
#endif
CGImageRelease(imageRef);
NSTimeInterval duration = [self sd_frameDurationWithIterator:iter];
SDImageFrame *frame = [SDImageFrame frameWithImage:image duration:duration];
[frames addObject:frame];
}
} while (WebPDemuxNextFrame(&iter));
WebPDemuxReleaseIterator(&iter);
WebPDemuxDelete(demuxer);
CGContextRelease(canvas);
UIImage *animatedImage = [SDImageCoderHelper animatedImageWithFrames:frames];
animatedImage.sd_imageLoopCount = loopCount;
animatedImage.sd_imageFormat = SDImageFormatWebP;
return animatedImage;
}
#pragma mark - Progressive Decode
- (instancetype)initIncrementalWithOptions:(nullable SDImageCoderOptions *)options {
self = [super init];
if (self) {
// Progressive images need transparent, so always use premultiplied BGRA
_idec = WebPINewRGB(MODE_bgrA, NULL, 0, 0);
CGFloat scale = 1;
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
if (scaleFactor != nil) {
scale = [scaleFactor doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
}
return self;
}
- (void)updateIncrementalData:(NSData *)data finished:(BOOL)finished {
if (_finished) {
return;
}
_imageData = data;
_finished = finished;
VP8StatusCode status = WebPIUpdate(_idec, data.bytes, data.length);
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
return;
}
// libwebp current does not support progressive decoding for animated image, so no need to scan and update the frame information
}
- (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
UIImage *image;
int width = 0;
int height = 0;
int last_y = 0;
int stride = 0;
uint8_t *rgba = WebPIDecGetRGB(_idec, &last_y, &width, &height, &stride);
// last_y may be 0, means no enough bitmap data to decode, ignore this
if (width + height > 0 && last_y > 0 && height >= last_y) {
// Construct a UIImage from the decoded RGBA value array
size_t rgbaSize = last_y * stride;
CGDataProviderRef provider =
CGDataProviderCreateWithData(NULL, rgba, rgbaSize, NULL);
CGColorSpaceRef colorSpaceRef = [SDImageCoderHelper colorSpaceGetDeviceRGB];
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
size_t components = 4;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// Why to use last_y for image height is because of libwebp's bug (https://bugs.chromium.org/p/webp/issues/detail?id=362)
// It will not keep memory barrier safe on x86 architechure (macOS & iPhone simulator) but on ARM architecture (iPhone & iPad & tv & watch) it works great
// If different threads use WebPIDecGetRGB to grab rgba bitmap, it will contain the previous decoded bitmap data
// So this will cause our drawed image looks strange(above is the current part but below is the previous part)
// We only grab the last_y height and draw the last_y height instead of total height image
// Besides fix, this can enhance performance since we do not need to create extra bitmap
CGImageRef imageRef = CGImageCreate(width, last_y, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
CGDataProviderRelease(provider);
if (!imageRef) {
return nil;
}
CGContextRef canvas = CGBitmapContextCreate(NULL, width, height, 8, 0, [SDImageCoderHelper colorSpaceGetDeviceRGB], bitmapInfo);
if (!canvas) {
CGImageRelease(imageRef);
return nil;
}
// Only draw the last_y image height, keep remains transparent, in Core Graphics coordinate system
CGContextDrawImage(canvas, CGRectMake(0, height - last_y, width, last_y), imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
CGImageRelease(imageRef);
if (!newImageRef) {
CGContextRelease(canvas);
return nil;
}
CGFloat scale = _scale;
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
if (scaleFactor != nil) {
scale = [scaleFactor doubleValue];
if (scale < 1) {
scale = 1;
}
}
#if SD_UIKIT || SD_WATCH
image = [[UIImage alloc] initWithCGImage:newImageRef scale:scale orientation:UIImageOrientationUp];
#else
image = [[UIImage alloc] initWithCGImage:newImageRef scale:scale orientation:kCGImagePropertyOrientationUp];
#endif
image.sd_isDecoded = YES; // Already drawn on bitmap context above
image.sd_imageFormat = SDImageFormatWebP;
CGImageRelease(newImageRef);
CGContextRelease(canvas);
}
return image;
}
- (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter {
size_t canvasHeight = CGBitmapContextGetHeight(canvas);
CGFloat tmpX = iter.x_offset;
CGFloat tmpY = canvasHeight - iter.height - iter.y_offset;
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
CGContextClearRect(canvas, imageRect);
} else {
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment];
if (!imageRef) {
return;
}
BOOL shouldBlend = iter.blend_method == WEBP_MUX_BLEND;
// If not blend, cover the target image rect. (firstly clear then draw)
if (!shouldBlend) {
CGContextClearRect(canvas, imageRect);
}
CGContextDrawImage(canvas, imageRect, imageRef);
CGImageRelease(imageRef);
}
}
- (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter CF_RETURNS_RETAINED {
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment];
if (!imageRef) {
return nil;
}
size_t canvasHeight = CGBitmapContextGetHeight(canvas);
CGFloat tmpX = iter.x_offset;
CGFloat tmpY = canvasHeight - iter.height - iter.y_offset;
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
BOOL shouldBlend = iter.blend_method == WEBP_MUX_BLEND;
// If not blend, cover the target image rect. (firstly clear then draw)
if (!shouldBlend) {
CGContextClearRect(canvas, imageRect);
}
CGContextDrawImage(canvas, imageRect, imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
CGImageRelease(imageRef);
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
CGContextClearRect(canvas, imageRect);
}
return newImageRef;
}
- (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData CF_RETURNS_RETAINED {
WebPDecoderConfig config;
if (!WebPInitDecoderConfig(&config)) {
return nil;
}
if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) {
return nil;
}
BOOL hasAlpha = config.input.has_alpha;
// iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
// use this bitmapInfo, combined with right colorspace, even without decode, can still avoid extra CA::Render::copy_image(which marked `Color Copied Images` from Instruments)
WEBP_CSP_MODE colorspace = MODE_bgrA;
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
config.options.use_threads = 1;
config.output.colorspace = colorspace;
// Decode the WebP image data into a RGBA value array
if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
return nil;
}
int width = config.input.width;
int height = config.input.height;
if (config.options.use_scaling) {
width = config.options.scaled_width;
height = config.options.scaled_height;
}
// Construct a UIImage from the decoded RGBA value array
CGDataProviderRef provider =
CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData);
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
size_t bytesPerRow = config.output.u.RGBA.stride;
CGColorSpaceRef colorSpaceRef = [SDImageCoderHelper colorSpaceGetDeviceRGB];
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
CGDataProviderRelease(provider);
return imageRef;
}
- (NSTimeInterval)sd_frameDurationWithIterator:(WebPIterator)iter {
int duration = iter.duration;
if (duration <= 10) {
// WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms
// Some animated WebP images also created without duration, we should keep compatibility
duration = 100;
}
return duration / 1000.0;
}
#pragma mark - Encode
- (BOOL)canEncodeToFormat:(SDImageFormat)format {
return (format == SDImageFormatWebP);
}
- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format options:(nullable SDImageCoderOptions *)options {
if (!image) {
return nil;
}
NSData *data;
double compressionQuality = 1;
if (options[SDImageCoderEncodeCompressionQuality]) {
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue];
}
NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image];
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
if (encodeFirstFrame || frames.count == 0) {
// for static single webp image
data = [self sd_encodedWebpDataWithImage:image quality:compressionQuality];
} else {
// for animated webp image
WebPMux *mux = WebPMuxNew();
if (!mux) {
return nil;
}
for (size_t i = 0; i < frames.count; i++) {
SDImageFrame *currentFrame = frames[i];
NSData *webpData = [self sd_encodedWebpDataWithImage:currentFrame.image quality:compressionQuality];
int duration = currentFrame.duration * 1000;
WebPMuxFrameInfo frame = { .bitstream.bytes = webpData.bytes,
.bitstream.size = webpData.length,
.duration = duration,
.id = WEBP_CHUNK_ANMF,
.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND, // each frame will clear canvas
.blend_method = WEBP_MUX_NO_BLEND
};
if (WebPMuxPushFrame(mux, &frame, 0) != WEBP_MUX_OK) {
WebPMuxDelete(mux);
return nil;
}
}
int loopCount = (int)image.sd_imageLoopCount;
WebPMuxAnimParams params = { .bgcolor = 0,
.loop_count = loopCount
};
if (WebPMuxSetAnimationParams(mux, &params) != WEBP_MUX_OK) {
WebPMuxDelete(mux);
return nil;
}
WebPData outputData;
WebPMuxError error = WebPMuxAssemble(mux, &outputData);
WebPMuxDelete(mux);
if (error != WEBP_MUX_OK) {
return nil;
}
data = [NSData dataWithBytes:outputData.bytes length:outputData.size];
WebPDataClear(&outputData);
}
return data;
}
- (nullable NSData *)sd_encodedWebpDataWithImage:(nullable UIImage *)image quality:(double)quality {
if (!image) {
return nil;
}
NSData *webpData;
CGImageRef imageRef = image.CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
if (width == 0 || width > WEBP_MAX_DIMENSION) {
return nil;
}
if (height == 0 || height > WEBP_MAX_DIMENSION) {
return nil;
}
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL byteOrderNormal = NO;
switch (byteOrderInfo) {
case kCGBitmapByteOrderDefault: {
byteOrderNormal = YES;
} break;
case kCGBitmapByteOrder32Little: {
} break;
case kCGBitmapByteOrder32Big: {
byteOrderNormal = YES;
} break;
default: break;
}
// If we can not get bitmap buffer, early return
CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
if (!dataProvider) {
return nil;
}
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);
if (!dataRef) {
return nil;
}
uint8_t *rgba = NULL;
// We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage
if (byteOrderNormal && ((alphaInfo == kCGImageAlphaNone) || (alphaInfo == kCGImageAlphaLast))) {
// If the input CGImage is already RGB888/RGBA8888
rgba = (uint8_t *)CFDataGetBytePtr(dataRef);
} else {
// Convert all other cases to target color mode using vImage
vImageConverterRef convertor = NULL;
vImage_Error error = kvImageNoError;
vImage_CGImageFormat srcFormat = {
.bitsPerComponent = (uint32_t)CGImageGetBitsPerComponent(imageRef),
.bitsPerPixel = (uint32_t)CGImageGetBitsPerPixel(imageRef),
.colorSpace = CGImageGetColorSpace(imageRef),
.bitmapInfo = bitmapInfo
};
vImage_CGImageFormat destFormat = {
.bitsPerComponent = 8,
.bitsPerPixel = hasAlpha ? 32 : 24,
.colorSpace = [SDImageCoderHelper colorSpaceGetDeviceRGB],
.bitmapInfo = hasAlpha ? kCGImageAlphaLast | kCGBitmapByteOrderDefault : kCGImageAlphaNone | kCGBitmapByteOrderDefault // RGB888/RGBA8888 (Non-premultiplied to works for libwebp)
};
convertor = vImageConverter_CreateWithCGImageFormat(&srcFormat, &destFormat, NULL, kvImageNoFlags, &error);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
vImage_Buffer src = {
.data = (uint8_t *)CFDataGetBytePtr(dataRef),
.width = width,
.height = height,
.rowBytes = bytesPerRow
};
vImage_Buffer dest;
error = vImageBuffer_Init(&dest, height, width, destFormat.bitsPerPixel, kvImageNoFlags);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
// Convert input color mode to RGB888/RGBA8888
error = vImageConvert_AnyToAny(convertor, &src, &dest, NULL, kvImageNoFlags);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
rgba = dest.data; // Converted buffer
bytesPerRow = dest.rowBytes; // Converted bytePerRow
CFRelease(dataRef);
dataRef = NULL;
}
uint8_t *data = NULL; // Output WebP data
float qualityFactor = quality * 100; // WebP quality is 0-100
// Encode RGB888/RGBA8888 buffer to WebP data
size_t size;
if (hasAlpha) {
size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data);
} else {
size = WebPEncodeRGB(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data);
}
if (dataRef) {
CFRelease(dataRef); // free non-converted rgba buffer
dataRef = NULL;
} else {
free(rgba); // free converted rgba buffer
rgba = NULL;
}
if (size) {
// success
webpData = [NSData dataWithBytes:data length:size];
}
if (data) {
WebPFree(data);
}
return webpData;
}
static void FreeImageData(void *info, const void *data, size_t size) {
free((void *)data);
}
#pragma mark - SDAnimatedImageCoder
- (instancetype)initWithAnimatedImageData:(NSData *)data options:(nullable SDImageCoderOptions *)options {
if (!data) {
return nil;
}
if (self) {
WebPData webpData;
WebPDataInit(&webpData);
webpData.bytes = data.bytes;
webpData.size = data.length;
WebPDemuxer *demuxer = WebPDemux(&webpData);
if (!demuxer) {
return nil;
}
BOOL framesValid = [self scanAndCheckFramesValidWithDemuxer:demuxer];
if (!framesValid) {
WebPDemuxDelete(demuxer);
return nil;
}
CGFloat scale = 1;
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
if (scaleFactor != nil) {
scale = [scaleFactor doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
_demux = demuxer;
_imageData = data;
_currentBlendIndex = NSNotFound;
_lock = dispatch_semaphore_create(1);
}
return self;
}
- (BOOL)scanAndCheckFramesValidWithDemuxer:(WebPDemuxer *)demuxer {
if (!demuxer) {
return NO;
}
WebPIterator iter;
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
WebPDemuxReleaseIterator(&iter);
return NO;
}
uint32_t iterIndex = 0;
uint32_t lastBlendIndex = 0;
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
BOOL hasAnimation = flags & ANIMATION_FLAG;
BOOL hasAlpha = flags & ALPHA_FLAG;
int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
uint32_t frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
uint32_t loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
NSMutableArray<SDWebPCoderFrame *> *frames = [NSMutableArray array];
// We should loop all the frames and scan each frames' blendFromIndex for later decoding, this can also ensure all frames is valid
do {
SDWebPCoderFrame *frame = [[SDWebPCoderFrame alloc] init];
frame.index = iterIndex;
frame.duration = [self sd_frameDurationWithIterator:iter];
frame.width = iter.width;
frame.height = iter.height;
frame.hasAlpha = iter.has_alpha;
frame.dispose = iter.dispose_method;
frame.blend = iter.blend_method;
frame.offsetX = iter.x_offset;
frame.offsetY = canvasHeight - iter.y_offset - iter.height;
BOOL sizeEqualsToCanvas = (iter.width == canvasWidth && iter.height == canvasHeight);
BOOL offsetIsZero = (iter.x_offset == 0 && iter.y_offset == 0);
frame.isFullSize = (sizeEqualsToCanvas && offsetIsZero);
if ((!frame.blend || !frame.hasAlpha) && frame.isFullSize) {
lastBlendIndex = iterIndex;
frame.blendFromIndex = iterIndex;
} else {
if (frame.dispose && frame.isFullSize) {
frame.blendFromIndex = lastBlendIndex;
lastBlendIndex = iterIndex + 1;
} else {
frame.blendFromIndex = lastBlendIndex;
}
}
iterIndex++;
[frames addObject:frame];
} while (WebPDemuxNextFrame(&iter));
WebPDemuxReleaseIterator(&iter);
if (frames.count != frameCount) {
return NO;
}
_frames = [frames copy];
_hasAnimation = hasAnimation;
_hasAlpha = hasAlpha;
_canvasWidth = canvasWidth;
_canvasHeight = canvasHeight;
_frameCount = frameCount;
_loopCount = loopCount;
return YES;
}
- (NSData *)animatedImageData {
return _imageData;
}
- (NSUInteger)animatedImageLoopCount {
return _loopCount;
}
- (NSUInteger)animatedImageFrameCount {
return _frameCount;
}
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
if (index >= _frameCount) {
return 0;
}
return _frames[index].duration;
}
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
UIImage *image;
if (index >= _frameCount) {
return nil;
}
LOCKBLOCK({
image = [self safeAnimatedImageFrameAtIndex:index];
});
return image;
}
- (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
if (!_canvas) {
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= _hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
CGContextRef canvas = CGBitmapContextCreate(NULL, _canvasWidth, _canvasHeight, 8, 0, [SDImageCoderHelper colorSpaceGetDeviceRGB], bitmapInfo);
if (!canvas) {
return nil;
}
_canvas = canvas;
}
SDWebPCoderFrame *frame = _frames[index];
UIImage *image;
WebPIterator iter;
if (_currentBlendIndex + 1 == index) {
// If current blend index is equal to request index, normal serial process
_currentBlendIndex = index;
// libwebp's index start with 1
if (!WebPDemuxGetFrame(_demux, (int)(index + 1), &iter)) {
WebPDemuxReleaseIterator(&iter);
return nil;
}
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter];
if (!imageRef) {
return nil;
}
#if SD_UIKIT || SD_WATCH
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
#else
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
#endif
CGImageRelease(imageRef);
} else {
// Else, this can happen when one image set to different imageViews or one loop end. So we should clear the shared cavans.
if (_currentBlendIndex != NSNotFound) {
CGContextClearRect(_canvas, CGRectMake(0, 0, _canvasWidth, _canvasHeight));
}
_currentBlendIndex = index;
// Then, loop from the blend from index, draw each of previous frames on the canvas.
// We use do while loop to call `WebPDemuxNextFrame`(fast), only (startIndex == endIndex) need to create image instance
size_t startIndex = frame.blendFromIndex;
size_t endIndex = frame.index;
if (!WebPDemuxGetFrame(_demux, (int)(startIndex + 1), &iter)) {
WebPDemuxReleaseIterator(&iter);
return nil;
}
do {
@autoreleasepool {
if ((size_t)iter.frame_num == endIndex) {
[self sd_blendWebpImageWithCanvas:_canvas iterator:iter];
} else {
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter];
if (!imageRef) {
return nil;
}
#if SD_UIKIT || SD_WATCH
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
#else
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
#endif
CGImageRelease(imageRef);
}
}
} while ((size_t)iter.frame_num < (endIndex + 1) && WebPDemuxNextFrame(&iter));
}
WebPDemuxReleaseIterator(&iter);
return image;
}
@end
#endif

View File

@ -0,0 +1,27 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifdef SD_WEBP
#import <SDWebImage/SDWebImageCompat.h>
// This category is just use as a convenience method. For more detail control, use methods in `UIImage+MultiFormat.h` or directlly use `SDImageCoder`
@interface UIImage (WebP)
/**
Create a image from the WebP data.
This will create animated image if the data is Animated WebP. And will create a static image is the data is Static WebP.
@param data The WebP data
@return The created image
*/
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data;
@end
#endif

View File

@ -0,0 +1,25 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifdef SD_WEBP
#import "UIImage+WebP.h"
#import "SDImageWebPCoder.h"
@implementation UIImage (WebP)
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data {
if (!data) {
return nil;
}
return [[SDImageWebPCoder sharedCoder] decodedImageWithData:data options:0];
}
@end
#endif