Initial checkin of the project - minimal code + docs + Travis CI script
This commit is contained in:
commit
091a6fd658
|
@ -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/
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
||||||
|
github "rs/SDWebImage" ~> 5.0.0-beta
|
||||||
|
github "SDWebImage/libwebp-Xcode" >= 1.0.0
|
|
@ -0,0 +1,2 @@
|
||||||
|
github "SDWebImage/libwebp-Xcode" "1.0.0"
|
||||||
|
github "rs/SDWebImage" "5.0.0-beta2"
|
|
@ -0,0 +1,6 @@
|
||||||
|
platform :ios, '8.0'
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
target 'SDImageWebPCoderExample' do
|
||||||
|
pod 'SDImageWebPCoder', :path => '../'
|
||||||
|
end
|
|
@ -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 */;
|
||||||
|
}
|
7
Example/SDImageWebPCoderExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Example/SDImageWebPCoderExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:SDImageWebPCoderExample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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, ¶ms) != 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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue