parent
1fc35cc4fd
commit
9431fafde2
|
@ -58,6 +58,10 @@ pod 'SocketRocket'
|
|||
#数据库管理
|
||||
pod 'FMDB'
|
||||
|
||||
|
||||
#pod 'GPUImage'
|
||||
pod 'SDWebImageWebPCoder'
|
||||
|
||||
post_install do |installer|
|
||||
installer.generated_projects.each do |project|
|
||||
project.targets.each do |target|
|
||||
|
|
|
@ -30,6 +30,18 @@ PODS:
|
|||
- HXPhotoPicker/Default (3.3.2)
|
||||
- IQKeyboardManager (6.5.10)
|
||||
- libpag (4.3.59)
|
||||
- libwebp (1.3.2):
|
||||
- libwebp/demux (= 1.3.2)
|
||||
- libwebp/mux (= 1.3.2)
|
||||
- libwebp/sharpyuv (= 1.3.2)
|
||||
- libwebp/webp (= 1.3.2)
|
||||
- libwebp/demux (1.3.2):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.3.2):
|
||||
- libwebp/demux
|
||||
- libwebp/sharpyuv (1.3.2)
|
||||
- libwebp/webp (1.3.2):
|
||||
- libwebp/sharpyuv
|
||||
- LSTTimer (0.2.10)
|
||||
- Masonry (1.1.0)
|
||||
- MBProgressHUD (1.2.0)
|
||||
|
@ -43,6 +55,9 @@ PODS:
|
|||
- SDWebImage (5.13.4):
|
||||
- SDWebImage/Core (= 5.13.4)
|
||||
- SDWebImage/Core (5.13.4)
|
||||
- SDWebImageWebPCoder (0.9.1):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.13)
|
||||
- SocketRocket (0.6.0)
|
||||
- SSZipArchive (2.4.3)
|
||||
- "UITableView+FDTemplateLayoutCell (1.6)"
|
||||
|
@ -78,6 +93,7 @@ DEPENDENCIES:
|
|||
- SDAutoLayout
|
||||
- SDCycleScrollView
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SocketRocket
|
||||
- SSZipArchive
|
||||
- "UITableView+FDTemplateLayoutCell"
|
||||
|
@ -102,6 +118,7 @@ SPEC REPOS:
|
|||
- HXPhotoPicker
|
||||
- IQKeyboardManager
|
||||
- libpag
|
||||
- libwebp
|
||||
- LSTTimer
|
||||
- Masonry
|
||||
- MBProgressHUD
|
||||
|
@ -112,6 +129,7 @@ SPEC REPOS:
|
|||
- SDAutoLayout
|
||||
- SDCycleScrollView
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SocketRocket
|
||||
- SSZipArchive
|
||||
- "UITableView+FDTemplateLayoutCell"
|
||||
|
@ -136,6 +154,7 @@ SPEC CHECKSUMS:
|
|||
HXPhotoPicker: 3d0526bc46cd2a4510a777d5f57dfad8e683fcfd
|
||||
IQKeyboardManager: 45a1fa55c1a5b02c61ac0fd7fd5b62bb4ad20d97
|
||||
libpag: ec1b388cdd9c192becc79738382829862ee5743d
|
||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||
LSTTimer: caf8f02ff366ca175cf4c1778d26c166183c1b6f
|
||||
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
|
||||
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
|
||||
|
@ -146,6 +165,7 @@ SPEC CHECKSUMS:
|
|||
SDAutoLayout: 8bf6b3bbc8b01f775d4defbb4fda2ace805461be
|
||||
SDCycleScrollView: a0d74c3384caa72bdfc81470bdbc8c14b3e1fbcf
|
||||
SDWebImage: e5cc87bf736e60f49592f307bdf9e157189298a3
|
||||
SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
|
||||
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
"UITableView+FDTemplateLayoutCell": 5c949b4a5059c404b442926c0e80f81d10a2d66f
|
||||
|
@ -159,6 +179,6 @@ SPEC CHECKSUMS:
|
|||
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
|
||||
ZXSDK: 414bed508b670437a9bdf7c75d49816b8cb7b2d4
|
||||
|
||||
PODFILE CHECKSUM: 8ef4da9ac45712582ed65b616cefa16f2828abe1
|
||||
PODFILE CHECKSUM: 9484ac364cb0f03591e4032ea0d99936c1521f8c
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
|
|
|
@ -30,6 +30,18 @@ PODS:
|
|||
- HXPhotoPicker/Default (3.3.2)
|
||||
- IQKeyboardManager (6.5.10)
|
||||
- libpag (4.3.59)
|
||||
- libwebp (1.3.2):
|
||||
- libwebp/demux (= 1.3.2)
|
||||
- libwebp/mux (= 1.3.2)
|
||||
- libwebp/sharpyuv (= 1.3.2)
|
||||
- libwebp/webp (= 1.3.2)
|
||||
- libwebp/demux (1.3.2):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.3.2):
|
||||
- libwebp/demux
|
||||
- libwebp/sharpyuv (1.3.2)
|
||||
- libwebp/webp (1.3.2):
|
||||
- libwebp/sharpyuv
|
||||
- LSTTimer (0.2.10)
|
||||
- Masonry (1.1.0)
|
||||
- MBProgressHUD (1.2.0)
|
||||
|
@ -43,6 +55,9 @@ PODS:
|
|||
- SDWebImage (5.13.4):
|
||||
- SDWebImage/Core (= 5.13.4)
|
||||
- SDWebImage/Core (5.13.4)
|
||||
- SDWebImageWebPCoder (0.9.1):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.13)
|
||||
- SocketRocket (0.6.0)
|
||||
- SSZipArchive (2.4.3)
|
||||
- "UITableView+FDTemplateLayoutCell (1.6)"
|
||||
|
@ -78,6 +93,7 @@ DEPENDENCIES:
|
|||
- SDAutoLayout
|
||||
- SDCycleScrollView
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SocketRocket
|
||||
- SSZipArchive
|
||||
- "UITableView+FDTemplateLayoutCell"
|
||||
|
@ -102,6 +118,7 @@ SPEC REPOS:
|
|||
- HXPhotoPicker
|
||||
- IQKeyboardManager
|
||||
- libpag
|
||||
- libwebp
|
||||
- LSTTimer
|
||||
- Masonry
|
||||
- MBProgressHUD
|
||||
|
@ -112,6 +129,7 @@ SPEC REPOS:
|
|||
- SDAutoLayout
|
||||
- SDCycleScrollView
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SocketRocket
|
||||
- SSZipArchive
|
||||
- "UITableView+FDTemplateLayoutCell"
|
||||
|
@ -136,6 +154,7 @@ SPEC CHECKSUMS:
|
|||
HXPhotoPicker: 3d0526bc46cd2a4510a777d5f57dfad8e683fcfd
|
||||
IQKeyboardManager: 45a1fa55c1a5b02c61ac0fd7fd5b62bb4ad20d97
|
||||
libpag: ec1b388cdd9c192becc79738382829862ee5743d
|
||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||
LSTTimer: caf8f02ff366ca175cf4c1778d26c166183c1b6f
|
||||
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
|
||||
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
|
||||
|
@ -146,6 +165,7 @@ SPEC CHECKSUMS:
|
|||
SDAutoLayout: 8bf6b3bbc8b01f775d4defbb4fda2ace805461be
|
||||
SDCycleScrollView: a0d74c3384caa72bdfc81470bdbc8c14b3e1fbcf
|
||||
SDWebImage: e5cc87bf736e60f49592f307bdf9e157189298a3
|
||||
SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
|
||||
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||
"UITableView+FDTemplateLayoutCell": 5c949b4a5059c404b442926c0e80f81d10a2d66f
|
||||
|
@ -159,6 +179,6 @@ SPEC CHECKSUMS:
|
|||
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
|
||||
ZXSDK: 414bed508b670437a9bdf7c75d49816b8cb7b2d4
|
||||
|
||||
PODFILE CHECKSUM: 8ef4da9ac45712582ed65b616cefa16f2828abe1
|
||||
PODFILE CHECKSUM: 9484ac364cb0f03591e4032ea0d99936c1521f8c
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
|
|
File diff suppressed because it is too large
Load Diff
58
ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/SDWebImageWebPCoder.xcscheme
generated
Normal file
58
ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/SDWebImageWebPCoder.xcscheme
generated
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1953860EA9853AA2BC8022B242F08512"
|
||||
BuildableName = "SDWebImageWebPCoder.framework"
|
||||
BlueprintName = "SDWebImageWebPCoder"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
58
ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/libwebp.xcscheme
generated
Normal file
58
ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/libwebp.xcscheme
generated
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "47D2E85A78C25869BB13521D8561A638"
|
||||
BuildableName = "libwebp.framework"
|
||||
BlueprintName = "libwebp"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -8,232 +8,176 @@
|
|||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>DZNEmptyDataSet.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>FMDB.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>GTCommonSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>GTExtensionSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>GTSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>GYSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>HXPhotoPicker.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>IQKeyboardManager.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>9</integer>
|
||||
</dict>
|
||||
<key>LSTTimer.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>11</integer>
|
||||
</dict>
|
||||
<key>MBProgressHUD.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>13</integer>
|
||||
</dict>
|
||||
<key>MJRefresh.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>14</integer>
|
||||
</dict>
|
||||
<key>MOFSPickerManager.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>15</integer>
|
||||
</dict>
|
||||
<key>Masonry.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>12</integer>
|
||||
</dict>
|
||||
<key>Pods-ProductApp.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>16</integer>
|
||||
</dict>
|
||||
<key>PopupKit.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>17</integer>
|
||||
</dict>
|
||||
<key>Reachability.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
<key>SDAutoLayout.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>SDCycleScrollView.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>20</integer>
|
||||
</dict>
|
||||
<key>SDWebImage.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>21</integer>
|
||||
</dict>
|
||||
<key>SDWebImageWebPCoder.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>SSZipArchive.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>23</integer>
|
||||
</dict>
|
||||
<key>SocketRocket.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>22</integer>
|
||||
</dict>
|
||||
<key>UITableView+FDTemplateLayoutCell.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>24</integer>
|
||||
</dict>
|
||||
<key>UMAPM.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>25</integer>
|
||||
</dict>
|
||||
<key>UMCCommonLog.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>26</integer>
|
||||
</dict>
|
||||
<key>UMCommon.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>27</integer>
|
||||
</dict>
|
||||
<key>UMDevice.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>28</integer>
|
||||
</dict>
|
||||
<key>UMLink.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>29</integer>
|
||||
</dict>
|
||||
<key>WechatOpenSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>30</integer>
|
||||
</dict>
|
||||
<key>YYModel.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>31</integer>
|
||||
</dict>
|
||||
<key>YYText.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>32</integer>
|
||||
</dict>
|
||||
<key>ZXSDK.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>33</integer>
|
||||
</dict>
|
||||
<key>libpag.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
<key>libwebp.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -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,262 @@
|
|||
# SDWebImageWebPCoder
|
||||
|
||||
[![CI Status](http://img.shields.io/travis/SDWebImage/SDWebImageWebPCoder.svg?style=flat)](https://travis-ci.org/SDWebImage/SDWebImageWebPCoder)
|
||||
[![Version](https://img.shields.io/cocoapods/v/SDWebImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDWebImageWebPCoder)
|
||||
[![License](https://img.shields.io/cocoapods/l/SDWebImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDWebImageWebPCoder)
|
||||
[![Platform](https://img.shields.io/cocoapods/p/SDWebImageWebPCoder.svg?style=flat)](http://cocoapods.org/pods/SDWebImageWebPCoder)
|
||||
[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/)
|
||||
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/SDWebImage/SDWebImageWebPCoder)
|
||||
[![codecov](https://codecov.io/gh/SDWebImage/SDWebImageWebPCoder/branch/master/graph/badge.svg)](https://codecov.io/gh/SDWebImage/SDWebImageWebPCoder)
|
||||
|
||||
Starting with the SDWebImage 5.0 version, we moved the WebP support code and [libwebp](https://github.com/webmproject/libwebp) from the Core Repo to this stand-alone repo.
|
||||
|
||||
SDWebImageWebPCoder supports both WebP decoding and encoding, for Static WebP or Animated WebP as well.
|
||||
|
||||
## Requirements
|
||||
|
||||
+ iOS 9.0
|
||||
+ macOS 10.11
|
||||
+ tvOS 9.0
|
||||
+ watchOS 2.0
|
||||
+ Xcode 11.0
|
||||
|
||||
## Installation
|
||||
|
||||
#### CocoaPods
|
||||
|
||||
SDWebImageWebPCoder is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile:
|
||||
|
||||
```ruby
|
||||
pod 'SDWebImageWebPCoder'
|
||||
```
|
||||
|
||||
#### Carthage
|
||||
|
||||
SDWebImageWebPCoder is available through [Carthage](https://github.com/Carthage/Carthage).
|
||||
|
||||
```
|
||||
github "SDWebImage/SDWebImageWebPCoder"
|
||||
```
|
||||
|
||||
#### Swift Package Manager (Xcode 11+)
|
||||
|
||||
SDWebImageWebPCoder is available through [Swift Package Manager](https://swift.org/package-manager).
|
||||
|
||||
```swift
|
||||
let package = Package(
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/SDWebImage/SDWebImageWebPCoder.git", from: "0.3.0")
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Add Coder
|
||||
|
||||
Before using SDWebImage to load WebP images, you need to register the WebP Coder to your coders manager. This step is recommended to be done after your App launch (like AppDelegate method).
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// Add coder
|
||||
SDImageWebPCoder *webPCoder = [SDImageWebPCoder sharedCoder];
|
||||
[[SDImageCodersManager sharedManager] addCoder:webPCoder];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// Add coder
|
||||
let WebPCoder = SDImageWebPCoder.shared
|
||||
SDImageCodersManager.shared.addCoder(WebPCoder)
|
||||
```
|
||||
|
||||
### Modify HTTP Accept Header
|
||||
|
||||
Some of image server provider may try to detect the client supported format, by default, SDWebImage use `image/*,*/*;q=0.8` for [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept). You can modify it with the `image/webp` as well.
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
[[SDWebImageDownloader sharedDownloader] setValue:@"image/webp,image/*,*/*;q=0.8" forHTTPHeaderField:@"Accept"];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
SDWebImageDownloader.shared.setValue("image/webp,image/*,*/*;q=0.8", forHTTPHeaderField:"Accept")
|
||||
```
|
||||
|
||||
### Loading
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// WebP online image loading
|
||||
NSURL *webpURL;
|
||||
UIImageView *imageView;
|
||||
[imageView sd_setImageWithURL:webpURL];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP online image loading
|
||||
let webpURL: URL
|
||||
let imageView: UIImageView
|
||||
imageView.sd_setImage(with: webpURL)
|
||||
```
|
||||
|
||||
### Progressive Animation Loading (0.5.0+)
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// WebP progressive loading for animated image
|
||||
NSURL *webpURL;
|
||||
SDAnimatedImageView *imageView;
|
||||
imageView.shouldIncrementalLoad = YES;
|
||||
[imageView sd_setImageWithURL:webpURL placeholderImage:nil options:SDWebImageProgressiveLoad];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP progressive loading for animated image
|
||||
let webpURL: URL
|
||||
let imageView: SDAnimatedImageView
|
||||
imageView.shouldIncrementalLoad = true
|
||||
imageView.sd_setImage(with: webpURL, placeholderImage: nil, options: [.progressiveLoad])
|
||||
```
|
||||
|
||||
### Decoding
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// WebP image decoding
|
||||
NSData *webpData;
|
||||
UIImage *image = [[SDImageWebPCoder sharedCoder] decodedImageWithData:webpData options:nil];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP image decoding
|
||||
let webpData: Data
|
||||
let image = SDImageWebPCoder.shared.decodedImage(with: data, options: nil)
|
||||
```
|
||||
|
||||
### Thumbnail Decoding (0.4.0+)
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// WebP thumbnail image decoding
|
||||
NSData *webpData;
|
||||
CGSize thumbnailSize = CGSizeMake(300, 300);
|
||||
UIImage *thumbnailImage = [[SDImageWebPCoder sharedCoder] decodedImageWithData:webpData options:@{SDImageCoderDecodeThumbnailPixelSize : @(thumbnailSize}];
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP thumbnail image decoding
|
||||
let webpData: Data
|
||||
let thumbnailSize = CGSize(width: 300, height: 300)
|
||||
let image = SDImageWebPCoder.shared.decodedImage(with: data, options: [.decodeThumbnailPixelSize: thumbnailSize])
|
||||
```
|
||||
|
||||
### Encoding
|
||||
|
||||
+ Objective-c
|
||||
|
||||
```objective-c
|
||||
// WebP image encoding
|
||||
UIImage *image;
|
||||
NSData *webpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatWebP options:nil];
|
||||
// Encode Quality
|
||||
NSData *lossyWebpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatWebP options:@{SDImageCoderEncodeCompressionQuality : @(0.1)}]; // [0, 1] compression quality
|
||||
NSData *limitedWebpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatWebP options:@{SDImageCoderEncodeMaxFileSize : @(1024 * 10)}]; // v0.6.0 feature, limit output file size <= 10KB
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP image encoding
|
||||
let image: UIImage
|
||||
let webpData = SDImageWebPCoder.shared.encodedData(with: image, format: .webP, options: nil)
|
||||
// Encode Quality
|
||||
let lossyWebpData = SDImageWebPCoder.shared.encodedData(with: image, format: .webP, options: [.encodeCompressionQuality: 0.1]) // [0, 1] compression quality
|
||||
let limitedWebpData = SDImageWebPCoder.shared.encodedData(with: image, format: .webP, options: [.encodeMaxFileSize: 1024 * 10]) // v0.6.0 feature, limit output file size <= 10KB
|
||||
```
|
||||
|
||||
### Thumbnail Encoding (0.6.1+)
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
// WebP image thumbnail encoding
|
||||
UIImage *image;
|
||||
NSData *thumbnailWebpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:image format:SDImageFormatWebP options:@{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(200, 200)}]; // v0.6.1 feature, encoding max pixel size
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
// WebP image thumbnail encoding
|
||||
let image: UIImage
|
||||
let thumbnailWebpData = SDImageWebPCoder.shared.encodedData(with: image, format: .webP, options: [.encodeMaxPixelSize: CGSize(width: 200, height: 200)]) // v0.6.1 feature, encoding max pixel size
|
||||
```
|
||||
|
||||
See more documentation in [SDWebImage Wiki - Coders](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage#custom-coder-420)
|
||||
|
||||
### Advanced WebP codec options (0.8+)
|
||||
|
||||
The WebP codec [libwebp](https://developers.google.com/speed/webp/docs/api) we use, supports some advanced control options for encoding/decoding. You can pass them to libwebp by using the wrapper top level API:
|
||||
|
||||
+ Objective-C
|
||||
|
||||
```objective-c
|
||||
UIImage *image;
|
||||
SDImageCoderOptions *options = @{SDImageCoderEncodeWebPMethod: @(0), SDImageCoderEncodeWebPAlphaCompression: @(100)};
|
||||
NSData *data = [SDImageWebPCoder.sharedCoder encodedDataWithImage:image format:SDImageFormatWebP options:options];
|
||||
// Will translate into:
|
||||
// config->method = 0;
|
||||
// config->alpha_quality = 100;
|
||||
```
|
||||
|
||||
+ Swift
|
||||
|
||||
```swift
|
||||
let image: UIImage
|
||||
let options = [.encodeWebPMethod: 0, .encodeWebPAlphaCompression: 100]
|
||||
let data = SDImageWebPCoder.shared.encodedData(with: image, format: .webP, options: options)
|
||||
// Will translate into:
|
||||
// config->method = 0;
|
||||
// config->alpha_quality = 100;
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
To run the example project, clone the repo, and run `pod install` from the root directory first. Then open `SDWebImageWebPCoder.xcworkspace`.
|
||||
|
||||
This is a demo to show how to use `WebP` and animated `WebP` images via `SDWebImageWebPCoderExample` target.
|
||||
|
||||
## Screenshot
|
||||
|
||||
<img src="https://raw.githubusercontent.com/SDWebImage/SDWebImageWebPCoder/master/Example/Screenshot/WebPDemo.png" width="300" />
|
||||
|
||||
These WebP images are from [WebP Gallery](https://developers.google.com/speed/webp/gallery1) and [GIF vs APNG vs WebP](http://littlesvr.ca/apng/gif_apng_webp.html)
|
||||
|
||||
## Author
|
||||
|
||||
[Bogdan Poplauschi](https://github.com/bpoplauschi)
|
||||
[DreamPiggy](https://github.com/dreampiggy)
|
||||
|
||||
## License
|
||||
|
||||
SDWebImageWebPCoder is available under the MIT license. See [the LICENSE file](https://github.com/SDWebImage/SDWebImageWebPCoder/blob/master/LICENSE) for more info.
|
||||
|
||||
|
22
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.h
generated
Normal file
22
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.h
generated
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if __has_include(<SDWebImage/SDWebImage.h>)
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
#else
|
||||
@import SDWebImage;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Built in coder that supports WebP and animated WebP
|
||||
*/
|
||||
@interface SDImageWebPCoder : NSObject <SDProgressiveImageCoder, SDAnimatedImageCoder>
|
||||
|
||||
@property (nonatomic, class, readonly, nonnull) SDImageWebPCoder *sharedCoder;
|
||||
|
||||
@end
|
1230
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
generated
Normal file
1230
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
135
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDWebImageWebPCoderDefine.h
generated
Normal file
135
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDWebImageWebPCoderDefine.h
generated
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if __has_include(<SDWebImage/SDWebImage.h>)
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
#else
|
||||
@import SDWebImage;
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Quality/speed trade-off (0=fast, 6=slower-better)
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPMethod;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Number of entropy-analysis passes (in [1..10])
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPass;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Preprocessing filter (0=none, 1=segment-smooth)
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPreprocessing;
|
||||
|
||||
/**
|
||||
Float value
|
||||
if non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target_size.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPTargetPSNR;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
If non-zero, try and use multi-threaded encoding.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPThreadLevel;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
If set, reduce memory usage (but increase CPU use).
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPLowMemory;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
if non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target_size.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPSegments;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Spatial Noise Shaping. 0=off, 100=maximum.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPSnsStrength;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Range: [0 = off .. 100 = strongest]
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterStrength;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
range: [0 = off .. 7 = least sharp]
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterSharpness;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Filtering type: 0 = simple, 1 = strong (only used If filter_strength > 0 or autofilter > 0)
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterType;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Auto adjust filter's strength [0 = off, 1 = on]
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAutofilter;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Algorithm for encoding the alpha plane (0 = none, 1 = compressed with WebP lossless). Default is 1.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaCompression;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Predictive filtering method for alpha plane. 0: none, 1: fast, 2: best. Default if 1.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaFiltering;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Between 0 (smallest size) and 100 (lossless).
|
||||
Default is 100.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaQuality;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
If true, export the compressed picture back.
|
||||
In-loop filtering is not applied.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPShowCompressed;
|
||||
|
||||
/**
|
||||
Integer
|
||||
Log2(number of token partitions) in [0..3]
|
||||
Default is set to 0 for easier progressive decoding.
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPartitions;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
Quality degradation allowed to fit the 512k limit on
|
||||
Prediction modes coding (0: no degradation, 100: maximum possible degradation).
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPartitionLimit;
|
||||
|
||||
/**
|
||||
Integer value
|
||||
if needed, use sharp (and slow) RGB->YUV conversion
|
||||
*/
|
||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPUseSharpYuv;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
28
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDWebImageWebPCoderDefine.m
generated
Normal file
28
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/SDWebImageWebPCoderDefine.m
generated
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// SDWebImageWebPCoderDefine.m
|
||||
// SDWebImageWebPCoder
|
||||
//
|
||||
// Created by Antti Kortetmaa on 2020/12/06.
|
||||
//
|
||||
|
||||
#import "SDWebImageWebPCoderDefine.h"
|
||||
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPMethod = @"webPMethod";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPass = @"webPPass";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPreprocessing = @"webPPreprocessing";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPThreadLevel = @"webPThreadLevel";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPLowMemory = @"webPLowMemory";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPTargetPSNR = @"webPTargetPSNR";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPSegments = @"webPSegments";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPSnsStrength = @"webPSnsStrength";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterStrength = @"webPFilterStrength";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterSharpness = @"webPFilterSharpness";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPFilterType = @"webPFilterType";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAutofilter = @"webPAutofilter";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaCompression = @"webPAlphaCompression";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaFiltering = @"webPAlphaFiltering";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPAlphaQuality = @"webPAlphaQuality";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPShowCompressed = @"webPShowCompressed";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPartitions = @"webPPartitions";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPPartitionLimit = @"webPPartitionLimit";
|
||||
SDImageCoderOption _Nonnull const SDImageCoderEncodeWebPUseSharpYuv = @"webPUseSharpYuv";
|
27
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/UIImage+WebP.h
generated
Normal file
27
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/UIImage+WebP.h
generated
Normal 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.
|
||||
*/
|
||||
|
||||
#if __has_include(<SDWebImage/SDWebImage.h>)
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
#else
|
||||
@import SDWebImage;
|
||||
#endif
|
||||
|
||||
// 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
|
21
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/UIImage+WebP.m
generated
Normal file
21
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Classes/UIImage+WebP.m
generated
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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
|
16
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Module/SDWebImageWebPCoder.h
generated
Normal file
16
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Module/SDWebImageWebPCoder.h
generated
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXPORT double SDWebImageWebPCoderVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char SDWebImageWebPCoderVersionString[];
|
||||
|
||||
#import <SDWebImageWebPCoder/SDImageWebPCoder.h>
|
||||
#import <SDWebImageWebPCoder/SDWebImageWebPCoderDefine.h>
|
||||
#import <SDWebImageWebPCoder/UIImage+WebP.h>
|
6
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Module/SDWebImageWebPCoder.modulemap
generated
Normal file
6
ProductApp/Pods/SDWebImageWebPCoder/SDWebImageWebPCoder/Module/SDWebImageWebPCoder.modulemap
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
framework module SDWebImageWebPCoder {
|
||||
umbrella header "SDWebImageWebPCoder.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
|
@ -768,6 +768,29 @@ THE SOFTWARE.
|
|||
|
||||
|
||||
|
||||
## SDWebImageWebPCoder
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## SSZipArchive
|
||||
|
||||
Copyright (c) 2013-2021, ZipArchive, https://github.com/ZipArchive
|
||||
|
@ -1000,4 +1023,38 @@ You may add Your own copyright statement to Your modifications and may provide a
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
## libwebp
|
||||
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Generated by CocoaPods - https://cocoapods.org
|
||||
|
|
|
@ -893,6 +893,35 @@ THE SOFTWARE.
|
|||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>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.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>SDWebImageWebPCoder</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Copyright (c) 2013-2021, ZipArchive, https://github.com/ZipArchive
|
||||
|
@ -1198,6 +1227,46 @@ END OF TERMS AND CONDITIONS
|
|||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>BSD</string>
|
||||
<key>Title</key>
|
||||
<string>libwebp</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Generated by CocoaPods - https://cocoapods.org</string>
|
||||
|
|
|
@ -14,9 +14,11 @@ ${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework
|
|||
${BUILT_PRODUCTS_DIR}/SDAutoLayout/SDAutoLayout.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework
|
||||
${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework
|
||||
${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework
|
||||
${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework
|
||||
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
|
||||
${BUILT_PRODUCTS_DIR}/YYText/YYText.framework
|
||||
${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework
|
||||
${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag/libpag.framework/libpag
|
|
@ -13,9 +13,11 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDAutoLayout.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDCycleScrollView.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UITableView_FDTemplateLayoutCell.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libpag.framework
|
|
@ -14,9 +14,11 @@ ${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework
|
|||
${BUILT_PRODUCTS_DIR}/SDAutoLayout/SDAutoLayout.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework
|
||||
${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework
|
||||
${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework
|
||||
${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework
|
||||
${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework
|
||||
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
|
||||
${BUILT_PRODUCTS_DIR}/YYText/YYText.framework
|
||||
${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework
|
||||
${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag/libpag.framework/libpag
|
|
@ -13,9 +13,11 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDAutoLayout.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDCycleScrollView.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UITableView_FDTemplateLayoutCell.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libpag.framework
|
|
@ -191,11 +191,13 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
|
|||
install_framework "${BUILT_PRODUCTS_DIR}/SDAutoLayout/SDAutoLayout.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework"
|
||||
install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag/libpag.framework"
|
||||
fi
|
||||
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||
|
@ -214,11 +216,13 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
|
|||
install_framework "${BUILT_PRODUCTS_DIR}/SDAutoLayout/SDAutoLayout.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework"
|
||||
install_framework "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag/libpag.framework"
|
||||
fi
|
||||
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText" "${PODS_ROOT}/GTCommonSDK" "${PODS_ROOT}/GTExtensionSDK" "${PODS_ROOT}/GTSDK" "${PODS_ROOT}/GYSDK" "${PODS_ROOT}/UMAPM/UMAPM_1.9.3" "${PODS_ROOT}/UMCCommonLog/UMCommonLog" "${PODS_ROOT}/UMCommon/UMCommon_7.4.7" "${PODS_ROOT}/UMDevice/UMDevice_3.4.0" "${PODS_ROOT}/UMLink" "${PODS_ROOT}/ZXSDK" "${PODS_ROOT}/libpag/framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTCommonSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTExtensionSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMAPM" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMCommon" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMDevice" "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker/HXPhotoPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket/SocketRocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/WechatOpenSDK"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_ROOT}/GTCommonSDK" "${PODS_ROOT}/GTExtensionSDK" "${PODS_ROOT}/GTSDK" "${PODS_ROOT}/GYSDK" "${PODS_ROOT}/UMAPM/UMAPM_1.9.3" "${PODS_ROOT}/UMCCommonLog/UMCommonLog" "${PODS_ROOT}/UMCommon/UMCommon_7.4.7" "${PODS_ROOT}/UMDevice/UMDevice_3.4.0" "${PODS_ROOT}/UMLink" "${PODS_ROOT}/ZXSDK" "${PODS_ROOT}/libpag/framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTCommonSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTExtensionSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMAPM" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMCommon" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMDevice" "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) SD_WEBP=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker/HXPhotoPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket/SocketRocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/WechatOpenSDK"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/WechatOpenSDK/OpenSDK2.0.4" "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"WechatOpenSDK" -l"c++" -l"iconv" -l"icucore" -l"resolv" -l"sqlite3" -l"sqlite3.0" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "CFNetwork" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "FMDB" -framework "Foundation" -framework "GTCommonSDK" -framework "GTExtensionSDK" -framework "GTSDK" -framework "GeYanSdk" -framework "HXPhotoPicker" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "Photos" -framework "PhotosUI" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SocketRocket" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "UMAPM" -framework "UMCommon" -framework "UMCommonLog" -framework "UMDevice" -framework "UMLink" -framework "VideoToolbox" -framework "WebKit" -framework "YYModel" -framework "YYText" -framework "ZXSDK" -framework "libpag" -weak_framework "AppTrackingTransparency" -weak_framework "Network" -weak_framework "UserNotifications"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"WechatOpenSDK" -l"c++" -l"iconv" -l"icucore" -l"resolv" -l"sqlite3" -l"sqlite3.0" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "CFNetwork" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "FMDB" -framework "Foundation" -framework "GTCommonSDK" -framework "GTExtensionSDK" -framework "GTSDK" -framework "GeYanSdk" -framework "HXPhotoPicker" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "Photos" -framework "PhotosUI" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SDWebImageWebPCoder" -framework "SSZipArchive" -framework "Security" -framework "SocketRocket" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "UMAPM" -framework "UMCommon" -framework "UMCommonLog" -framework "UMDevice" -framework "UMLink" -framework "VideoToolbox" -framework "WebKit" -framework "YYModel" -framework "YYText" -framework "ZXSDK" -framework "libpag" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Network" -weak_framework "UserNotifications"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/libwebp/src
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
VALID_ARCHS = arm64 armv7 x86_64
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText" "${PODS_ROOT}/GTCommonSDK" "${PODS_ROOT}/GTExtensionSDK" "${PODS_ROOT}/GTSDK" "${PODS_ROOT}/GYSDK" "${PODS_ROOT}/UMAPM/UMAPM_1.9.3" "${PODS_ROOT}/UMCCommonLog/UMCommonLog" "${PODS_ROOT}/UMCommon/UMCommon_7.4.7" "${PODS_ROOT}/UMDevice/UMDevice_3.4.0" "${PODS_ROOT}/UMLink" "${PODS_ROOT}/ZXSDK" "${PODS_ROOT}/libpag/framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTCommonSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTExtensionSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMAPM" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMCommon" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMDevice" "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker/HXPhotoPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket/SocketRocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/WechatOpenSDK"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_ROOT}/GTCommonSDK" "${PODS_ROOT}/GTExtensionSDK" "${PODS_ROOT}/GTSDK" "${PODS_ROOT}/GYSDK" "${PODS_ROOT}/UMAPM/UMAPM_1.9.3" "${PODS_ROOT}/UMCCommonLog/UMCommonLog" "${PODS_ROOT}/UMCommon/UMCommon_7.4.7" "${PODS_ROOT}/UMDevice/UMDevice_3.4.0" "${PODS_ROOT}/UMLink" "${PODS_ROOT}/ZXSDK" "${PODS_ROOT}/libpag/framework" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTCommonSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTExtensionSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GTSDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMAPM" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMCommon" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UMDevice" "${PODS_XCFRAMEWORKS_BUILD_DIR}/libpag"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) SD_WEBP=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPhotoPicker/HXPhotoPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SocketRocket/SocketRocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/WechatOpenSDK"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/WechatOpenSDK/OpenSDK2.0.4" "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"WechatOpenSDK" -l"c++" -l"iconv" -l"icucore" -l"resolv" -l"sqlite3" -l"sqlite3.0" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "CFNetwork" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "FMDB" -framework "Foundation" -framework "GTCommonSDK" -framework "GTExtensionSDK" -framework "GTSDK" -framework "GeYanSdk" -framework "HXPhotoPicker" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "Photos" -framework "PhotosUI" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SocketRocket" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "UMAPM" -framework "UMCommon" -framework "UMCommonLog" -framework "UMDevice" -framework "UMLink" -framework "VideoToolbox" -framework "WebKit" -framework "YYModel" -framework "YYText" -framework "ZXSDK" -framework "libpag" -weak_framework "AppTrackingTransparency" -weak_framework "Network" -weak_framework "UserNotifications"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"WechatOpenSDK" -l"c++" -l"iconv" -l"icucore" -l"resolv" -l"sqlite3" -l"sqlite3.0" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "CFNetwork" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "FMDB" -framework "Foundation" -framework "GTCommonSDK" -framework "GTExtensionSDK" -framework "GTSDK" -framework "GeYanSdk" -framework "HXPhotoPicker" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "Photos" -framework "PhotosUI" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SDWebImageWebPCoder" -framework "SSZipArchive" -framework "Security" -framework "SocketRocket" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "UMAPM" -framework "UMCommon" -framework "UMCommonLog" -framework "UMDevice" -framework "UMLink" -framework "VideoToolbox" -framework "WebKit" -framework "YYModel" -framework "YYText" -framework "ZXSDK" -framework "libpag" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Network" -weak_framework "UserNotifications"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/libwebp/src
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
VALID_ARCHS = arm64 armv7 x86_64
|
||||
|
|
26
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-Info.plist
generated
Normal file
26
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-Info.plist
generated
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>${PODS_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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.9.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
5
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-dummy.m
generated
Normal file
5
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-dummy.m
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_SDWebImageWebPCoder : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_SDWebImageWebPCoder
|
||||
@end
|
12
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-prefix.pch
generated
Normal file
12
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder-prefix.pch
generated
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
15
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.debug.xcconfig
generated
Normal file
15
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.debug.xcconfig
generated
Normal file
|
@ -0,0 +1,15 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) SD_WEBP=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "ImageIO" -framework "SDWebImage" -framework "libwebp"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SDWebImageWebPCoder
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/libwebp/src
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
6
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.modulemap
generated
Normal file
6
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.modulemap
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
framework module SDWebImageWebPCoder {
|
||||
umbrella header "SDWebImageWebPCoder.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
15
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.release.xcconfig
generated
Normal file
15
ProductApp/Pods/Target Support Files/SDWebImageWebPCoder/SDWebImageWebPCoder.release.xcconfig
generated
Normal file
|
@ -0,0 +1,15 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) SD_WEBP=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "ImageIO" -framework "SDWebImage" -framework "libwebp"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SDWebImageWebPCoder
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/libwebp/src
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
|
@ -0,0 +1,26 @@
|
|||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>${PODS_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>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_libwebp : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_libwebp
|
||||
@end
|
|
@ -0,0 +1,12 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "demux.h"
|
||||
#import "mux.h"
|
||||
#import "sharpyuv.h"
|
||||
#import "decode.h"
|
||||
#import "encode.h"
|
||||
#import "types.h"
|
||||
#import "mux_types.h"
|
||||
#import "format_constants.h"
|
||||
|
||||
FOUNDATION_EXPORT double libwebpVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char libwebpVersionString[];
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/libwebp
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/libwebp
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) ${PODS_ROOT}/libwebp/ ${PODS_TARGET_SRCROOT}/
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
|
@ -0,0 +1,6 @@
|
|||
framework module libwebp {
|
||||
umbrella header "libwebp-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/libwebp
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/libwebp
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USER_HEADER_SEARCH_PATHS = $(inherited) ${PODS_ROOT}/libwebp/ ${PODS_TARGET_SRCROOT}/
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 ibireme <ibireme@gmail.com>
|
||||
|
||||
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,171 @@
|
|||
YYCache
|
||||
==============
|
||||
|
||||
[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYCache/master/LICENSE)
|
||||
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
|
||||
[![CocoaPods](http://img.shields.io/cocoapods/v/YYCache.svg?style=flat)](http://cocoapods.org/?q= YYCache)
|
||||
[![CocoaPods](http://img.shields.io/cocoapods/p/YYCache.svg?style=flat)](http://cocoapods.org/?q= YYCache)
|
||||
[![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)
|
||||
[![Build Status](https://travis-ci.org/ibireme/YYCache.svg?branch=master)](https://travis-ci.org/ibireme/YYCache)
|
||||
|
||||
High performance cache framework for iOS.<br/>
|
||||
(It's a component of [YYKit](https://github.com/ibireme/YYKit))
|
||||
|
||||
Performance
|
||||
==============
|
||||
|
||||
![Memory cache benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_memory.png
|
||||
)
|
||||
|
||||
![Disk benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_disk.png
|
||||
)
|
||||
|
||||
You may [download](http://www.sqlite.org/download.html) and compile the latest version of sqlite and ignore the libsqlite3.dylib in iOS system to get higher performance.
|
||||
|
||||
See `Benchmark/CacheBenchmark.xcodeproj` for more benchmark case.
|
||||
|
||||
|
||||
Features
|
||||
==============
|
||||
- **LRU**: Objects can be evicted with least-recently-used algorithm.
|
||||
- **Limitation**: Cache limitation can be controlled with count, cost, age and free space.
|
||||
- **Compatibility**: The API is similar to `NSCache`, all methods are thread-safe.
|
||||
- **Memory Cache**
|
||||
- **Release Control**: Objects can be released synchronously/asynchronously on main thread or background thread.
|
||||
- **Automatically Clear**: It can be configured to automatically evict objects when receive memory warning or app enter background.
|
||||
- **Disk Cache**
|
||||
- **Customization**: It supports custom archive and unarchive method to store object which does not adopt NSCoding.
|
||||
- **Storage Type Control**: It can automatically decide the storage type (sqlite / file) for each object to get
|
||||
better performance.
|
||||
|
||||
|
||||
Installation
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. Add `pod 'YYCache'` to your Podfile.
|
||||
2. Run `pod install` or `pod update`.
|
||||
3. Import \<YYCache/YYCache.h\>.
|
||||
|
||||
|
||||
### Carthage
|
||||
|
||||
1. Add `github "ibireme/YYCache"` to your Cartfile.
|
||||
2. Run `carthage update --platform ios` and add the framework to your project.
|
||||
3. Import \<YYCache/YYCache.h\>.
|
||||
|
||||
|
||||
### Manually
|
||||
|
||||
1. Download all the files in the YYCache subdirectory.
|
||||
2. Add the source files to your Xcode project.
|
||||
3. Link with required frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* sqlite3
|
||||
4. Import `YYCache.h`.
|
||||
|
||||
|
||||
Documentation
|
||||
==============
|
||||
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYCache/).<br/>
|
||||
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
|
||||
|
||||
|
||||
Requirements
|
||||
==============
|
||||
This library requires `iOS 6.0+` and `Xcode 7.0+`.
|
||||
|
||||
|
||||
License
|
||||
==============
|
||||
YYCache is provided under the MIT license. See LICENSE file for details.
|
||||
|
||||
|
||||
<br/><br/>
|
||||
---
|
||||
中文介绍
|
||||
==============
|
||||
高性能 iOS 缓存框架。<br/>
|
||||
(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一)
|
||||
|
||||
性能
|
||||
==============
|
||||
|
||||
iPhone 6 上,内存缓存每秒响应次数 (越高越好):
|
||||
![Memory cache benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_memory.png
|
||||
)
|
||||
|
||||
iPhone 6 上,磁盘缓存每秒响应次数 (越高越好):
|
||||
![Disk benchmark result](https://raw.github.com/ibireme/YYCache/master/Benchmark/Result_disk.png
|
||||
)
|
||||
|
||||
推荐到 SQLite 官网[下载](http://www.sqlite.org/download.html)和编译最新的 SQLite,以替换 iOS 自带的 libsqlite3.dylib,以获得最高 1.5~3 倍的性能提升。
|
||||
|
||||
更多测试代码和用例见 `Benchmark/CacheBenchmark.xcodeproj`。
|
||||
|
||||
|
||||
特性
|
||||
==============
|
||||
- **LRU**: 缓存支持 LRU (least-recently-used) 淘汰算法。
|
||||
- **缓存控制**: 支持多种缓存控制方法:总数量、总大小、存活时间、空闲空间。
|
||||
- **兼容性**: API 基本和 `NSCache` 保持一致, 所有方法都是线程安全的。
|
||||
- **内存缓存**
|
||||
- **对象释放控制**: 对象的释放(release) 可以配置为同步或异步进行,可以配置在主线程或后台线程进行。
|
||||
- **自动清空**: 当收到内存警告或 App 进入后台时,缓存可以配置为自动清空。
|
||||
- **磁盘缓存**
|
||||
- **可定制性**: 磁盘缓存支持自定义的归档解档方法,以支持那些没有实现 NSCoding 协议的对象。
|
||||
- **存储类型控制**: 磁盘缓存支持对每个对象的存储类型 (SQLite/文件) 进行自动或手动控制,以获得更高的存取性能。
|
||||
|
||||
|
||||
安装
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. 在 Podfile 中添加 `pod 'YYCache'`。
|
||||
2. 执行 `pod install` 或 `pod update`。
|
||||
3. 导入 \<YYCache/YYCache.h\>。
|
||||
|
||||
|
||||
### Carthage
|
||||
|
||||
1. 在 Cartfile 中添加 `github "ibireme/YYCache"`。
|
||||
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
|
||||
3. 导入 \<YYCache/YYCache.h\>。
|
||||
|
||||
|
||||
### 手动安装
|
||||
|
||||
1. 下载 YYCache 文件夹内的所有内容。
|
||||
2. 将 YYCache 内的源文件添加(拖放)到你的工程。
|
||||
3. 链接以下的 frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* sqlite3
|
||||
4. 导入 `YYCache.h`。
|
||||
|
||||
|
||||
文档
|
||||
==============
|
||||
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYCache/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
|
||||
|
||||
|
||||
系统要求
|
||||
==============
|
||||
该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。
|
||||
|
||||
|
||||
许可证
|
||||
==============
|
||||
YYCache 使用 MIT 许可证,详情见 LICENSE 文件。
|
||||
|
||||
|
||||
相关链接
|
||||
==============
|
||||
[YYCache 设计思路与技术细节](http://blog.ibireme.com/2015/10/26/yycache/)
|
||||
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
//
|
||||
// YYCache.h
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/13.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<YYCache/YYCache.h>)
|
||||
FOUNDATION_EXPORT double YYCacheVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char YYCacheVersionString[];
|
||||
#import <YYCache/YYMemoryCache.h>
|
||||
#import <YYCache/YYDiskCache.h>
|
||||
#import <YYCache/YYKVStorage.h>
|
||||
#elif __has_include(<YYWebImage/YYCache.h>)
|
||||
#import <YYWebImage/YYMemoryCache.h>
|
||||
#import <YYWebImage/YYDiskCache.h>
|
||||
#import <YYWebImage/YYKVStorage.h>
|
||||
#else
|
||||
#import "YYMemoryCache.h"
|
||||
#import "YYDiskCache.h"
|
||||
#import "YYKVStorage.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
`YYCache` is a thread safe key-value cache.
|
||||
|
||||
It use `YYMemoryCache` to store objects in a small and fast memory cache,
|
||||
and use `YYDiskCache` to persisting objects to a large and slow disk cache.
|
||||
See `YYMemoryCache` and `YYDiskCache` for more information.
|
||||
*/
|
||||
@interface YYCache : NSObject
|
||||
|
||||
/** The name of the cache, readonly. */
|
||||
@property (copy, readonly) NSString *name;
|
||||
|
||||
/** The underlying memory cache. see `YYMemoryCache` for more information.*/
|
||||
@property (strong, readonly) YYMemoryCache *memoryCache;
|
||||
|
||||
/** The underlying disk cache. see `YYDiskCache` for more information.*/
|
||||
@property (strong, readonly) YYDiskCache *diskCache;
|
||||
|
||||
/**
|
||||
Create a new instance with the specified name.
|
||||
Multiple instances with the same name will make the cache unstable.
|
||||
|
||||
@param name The name of the cache. It will create a dictionary with the name in
|
||||
the app's caches dictionary for disk cache. Once initialized you should not
|
||||
read and write to this directory.
|
||||
@result A new cache object, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithName:(NSString *)name;
|
||||
|
||||
/**
|
||||
Create a new instance with the specified path.
|
||||
Multiple instances with the same name will make the cache unstable.
|
||||
|
||||
@param path Full path of a directory in which the cache will write data.
|
||||
Once initialized you should not read and write to this directory.
|
||||
@result A new cache object, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Convenience Initializers
|
||||
Create a new instance with the specified name.
|
||||
Multiple instances with the same name will make the cache unstable.
|
||||
|
||||
@param name The name of the cache. It will create a dictionary with the name in
|
||||
the app's caches dictionary for disk cache. Once initialized you should not
|
||||
read and write to this directory.
|
||||
@result A new cache object, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)cacheWithName:(NSString *)name;
|
||||
|
||||
/**
|
||||
Convenience Initializers
|
||||
Create a new instance with the specified path.
|
||||
Multiple instances with the same name will make the cache unstable.
|
||||
|
||||
@param path Full path of a directory in which the cache will write data.
|
||||
Once initialized you should not read and write to this directory.
|
||||
@result A new cache object, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)cacheWithPath:(NSString *)path;
|
||||
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
#pragma mark - Access Methods
|
||||
///=============================================================================
|
||||
/// @name Access Methods
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Returns a boolean value that indicates whether a given key is in cache.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return NO.
|
||||
@return Whether the key is in cache.
|
||||
*/
|
||||
- (BOOL)containsObjectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns a boolean value with the block that indicates whether a given key is in cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return NO.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)containsObjectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, BOOL contains))block;
|
||||
|
||||
/**
|
||||
Returns the value associated with a given key.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return nil.
|
||||
@return The value associated with key, or nil if no value is associated with key.
|
||||
*/
|
||||
- (nullable id<NSCoding>)objectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns the value associated with a given key.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return nil.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)objectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key, id<NSCoding> object))block;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache.
|
||||
This method may blocks the calling thread until file write finished.
|
||||
|
||||
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
|
||||
@param key The key with which to associate the value. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key withBlock:(nullable void(^)(void))block;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
This method may blocks the calling thread until file delete finished.
|
||||
|
||||
@param key The key identifying the value to be removed. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)removeObjectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key The key identifying the value to be removed. If nil, this method has no effect.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)removeObjectForKey:(NSString *)key withBlock:(nullable void(^)(NSString *key))block;
|
||||
|
||||
/**
|
||||
Empties the cache.
|
||||
This method may blocks the calling thread until file delete finished.
|
||||
*/
|
||||
- (void)removeAllObjects;
|
||||
|
||||
/**
|
||||
Empties the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)removeAllObjectsWithBlock:(void(^)(void))block;
|
||||
|
||||
/**
|
||||
Empties the cache with block.
|
||||
This method returns immediately and executes the clear operation with block in background.
|
||||
|
||||
@warning You should not send message to this instance in these blocks.
|
||||
@param progress This block will be invoked during removing, pass nil to ignore.
|
||||
@param end This block will be invoked at the end, pass nil to ignore.
|
||||
*/
|
||||
- (void)removeAllObjectsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
|
||||
endBlock:(nullable void(^)(BOOL error))end;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// YYCache.m
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/13.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYCache.h"
|
||||
#import "YYMemoryCache.h"
|
||||
#import "YYDiskCache.h"
|
||||
|
||||
@implementation YYCache
|
||||
|
||||
- (instancetype) init {
|
||||
NSLog(@"Use \"initWithName\" or \"initWithPath\" to create YYCache instance.");
|
||||
return [self initWithPath:@""];
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name {
|
||||
if (name.length == 0) return nil;
|
||||
NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
|
||||
NSString *path = [cacheFolder stringByAppendingPathComponent:name];
|
||||
return [self initWithPath:path];
|
||||
}
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path {
|
||||
if (path.length == 0) return nil;
|
||||
YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path];
|
||||
if (!diskCache) return nil;
|
||||
NSString *name = [path lastPathComponent];
|
||||
YYMemoryCache *memoryCache = [YYMemoryCache new];
|
||||
memoryCache.name = name;
|
||||
|
||||
self = [super init];
|
||||
_name = name;
|
||||
_diskCache = diskCache;
|
||||
_memoryCache = memoryCache;
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)cacheWithName:(NSString *)name {
|
||||
return [[self alloc] initWithName:name];
|
||||
}
|
||||
|
||||
+ (instancetype)cacheWithPath:(NSString *)path {
|
||||
return [[self alloc] initWithPath:path];
|
||||
}
|
||||
|
||||
- (BOOL)containsObjectForKey:(NSString *)key {
|
||||
return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
|
||||
}
|
||||
|
||||
- (void)containsObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key, BOOL contains))block {
|
||||
if (!block) return;
|
||||
|
||||
if ([_memoryCache containsObjectForKey:key]) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
block(key, YES);
|
||||
});
|
||||
} else {
|
||||
[_diskCache containsObjectForKey:key withBlock:block];
|
||||
}
|
||||
}
|
||||
|
||||
- (id<NSCoding>)objectForKey:(NSString *)key {
|
||||
id<NSCoding> object = [_memoryCache objectForKey:key];
|
||||
if (!object) {
|
||||
object = [_diskCache objectForKey:key];
|
||||
if (object) {
|
||||
[_memoryCache setObject:object forKey:key];
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void)objectForKey:(NSString *)key withBlock:(void (^)(NSString *key, id<NSCoding> object))block {
|
||||
if (!block) return;
|
||||
id<NSCoding> object = [_memoryCache objectForKey:key];
|
||||
if (object) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
block(key, object);
|
||||
});
|
||||
} else {
|
||||
[_diskCache objectForKey:key withBlock:^(NSString *key, id<NSCoding> object) {
|
||||
if (object && ![_memoryCache objectForKey:key]) {
|
||||
[_memoryCache setObject:object forKey:key];
|
||||
}
|
||||
block(key, object);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key {
|
||||
[_memoryCache setObject:object forKey:key];
|
||||
[_diskCache setObject:object forKey:key];
|
||||
}
|
||||
|
||||
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key withBlock:(void (^)(void))block {
|
||||
[_memoryCache setObject:object forKey:key];
|
||||
[_diskCache setObject:object forKey:key withBlock:block];
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key {
|
||||
[_memoryCache removeObjectForKey:key];
|
||||
[_diskCache removeObjectForKey:key];
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key))block {
|
||||
[_memoryCache removeObjectForKey:key];
|
||||
[_diskCache removeObjectForKey:key withBlock:block];
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
[_memoryCache removeAllObjects];
|
||||
[_diskCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)removeAllObjectsWithBlock:(void(^)(void))block {
|
||||
[_memoryCache removeAllObjects];
|
||||
[_diskCache removeAllObjectsWithBlock:block];
|
||||
}
|
||||
|
||||
- (void)removeAllObjectsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress
|
||||
endBlock:(void(^)(BOOL error))end {
|
||||
[_memoryCache removeAllObjects];
|
||||
[_diskCache removeAllObjectsWithProgressBlock:progress endBlock:end];
|
||||
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
|
||||
else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,412 @@
|
|||
//
|
||||
// YYDiskCache.h
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/11.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
YYDiskCache is a thread-safe cache that stores key-value pairs backed by SQLite
|
||||
and file system (similar to NSURLCache's disk cache).
|
||||
|
||||
YYDiskCache has these features:
|
||||
|
||||
* It use LRU (least-recently-used) to remove objects.
|
||||
* It can be controlled by cost, count, and age.
|
||||
* It can be configured to automatically evict objects when there's no free disk space.
|
||||
* It can automatically decide the storage type (sqlite/file) for each object to get
|
||||
better performance.
|
||||
|
||||
You may compile the latest version of sqlite and ignore the libsqlite3.dylib in
|
||||
iOS system to get 2x~4x speed up.
|
||||
*/
|
||||
@interface YYDiskCache : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
|
||||
/** The name of the cache. Default is nil. */
|
||||
@property (nullable, copy) NSString *name;
|
||||
|
||||
/** The path of the cache (read-only). */
|
||||
@property (readonly) NSString *path;
|
||||
|
||||
/**
|
||||
If the object's data size (in bytes) is larger than this value, then object will
|
||||
be stored as a file, otherwise the object will be stored in sqlite.
|
||||
|
||||
0 means all objects will be stored as separated files, NSUIntegerMax means all
|
||||
objects will be stored in sqlite.
|
||||
|
||||
The default value is 20480 (20KB).
|
||||
*/
|
||||
@property (readonly) NSUInteger inlineThreshold;
|
||||
|
||||
/**
|
||||
If this block is not nil, then the block will be used to archive object instead
|
||||
of NSKeyedArchiver. You can use this block to support the objects which do not
|
||||
conform to the `NSCoding` protocol.
|
||||
|
||||
The default value is nil.
|
||||
*/
|
||||
@property (nullable, copy) NSData *(^customArchiveBlock)(id object);
|
||||
|
||||
/**
|
||||
If this block is not nil, then the block will be used to unarchive object instead
|
||||
of NSKeyedUnarchiver. You can use this block to support the objects which do not
|
||||
conform to the `NSCoding` protocol.
|
||||
|
||||
The default value is nil.
|
||||
*/
|
||||
@property (nullable, copy) id (^customUnarchiveBlock)(NSData *data);
|
||||
|
||||
/**
|
||||
When an object needs to be saved as a file, this block will be invoked to generate
|
||||
a file name for a specified key. If the block is nil, the cache use md5(key) as
|
||||
default file name.
|
||||
|
||||
The default value is nil.
|
||||
*/
|
||||
@property (nullable, copy) NSString *(^customFileNameBlock)(NSString *key);
|
||||
|
||||
|
||||
|
||||
#pragma mark - Limit
|
||||
///=============================================================================
|
||||
/// @name Limit
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
The maximum number of objects the cache should hold.
|
||||
|
||||
@discussion The default value is NSUIntegerMax, which means no limit.
|
||||
This is not a strict limit — if the cache goes over the limit, some objects in the
|
||||
cache could be evicted later in background queue.
|
||||
*/
|
||||
@property NSUInteger countLimit;
|
||||
|
||||
/**
|
||||
The maximum total cost that the cache can hold before it starts evicting objects.
|
||||
|
||||
@discussion The default value is NSUIntegerMax, which means no limit.
|
||||
This is not a strict limit — if the cache goes over the limit, some objects in the
|
||||
cache could be evicted later in background queue.
|
||||
*/
|
||||
@property NSUInteger costLimit;
|
||||
|
||||
/**
|
||||
The maximum expiry time of objects in cache.
|
||||
|
||||
@discussion The default value is DBL_MAX, which means no limit.
|
||||
This is not a strict limit — if an object goes over the limit, the objects could
|
||||
be evicted later in background queue.
|
||||
*/
|
||||
@property NSTimeInterval ageLimit;
|
||||
|
||||
/**
|
||||
The minimum free disk space (in bytes) which the cache should kept.
|
||||
|
||||
@discussion The default value is 0, which means no limit.
|
||||
If the free disk space is lower than this value, the cache will remove objects
|
||||
to free some disk space. This is not a strict limit—if the free disk space goes
|
||||
over the limit, the objects could be evicted later in background queue.
|
||||
*/
|
||||
@property NSUInteger freeDiskSpaceLimit;
|
||||
|
||||
/**
|
||||
The auto trim check time interval in seconds. Default is 60 (1 minute).
|
||||
|
||||
@discussion The cache holds an internal timer to check whether the cache reaches
|
||||
its limits, and if the limit is reached, it begins to evict objects.
|
||||
*/
|
||||
@property NSTimeInterval autoTrimInterval;
|
||||
|
||||
/**
|
||||
Set `YES` to enable error logs for debug.
|
||||
*/
|
||||
@property BOOL errorLogsEnabled;
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Create a new cache based on the specified path.
|
||||
|
||||
@param path Full path of a directory in which the cache will write data.
|
||||
Once initialized you should not read and write to this directory.
|
||||
|
||||
@return A new cache object, or nil if an error occurs.
|
||||
|
||||
@warning If the cache instance for the specified path already exists in memory,
|
||||
this method will return it directly, instead of creating a new instance.
|
||||
*/
|
||||
- (nullable instancetype)initWithPath:(NSString *)path;
|
||||
|
||||
/**
|
||||
The designated initializer.
|
||||
|
||||
@param path Full path of a directory in which the cache will write data.
|
||||
Once initialized you should not read and write to this directory.
|
||||
|
||||
@param threshold The data store inline threshold in bytes. If the object's data
|
||||
size (in bytes) is larger than this value, then object will be stored as a
|
||||
file, otherwise the object will be stored in sqlite. 0 means all objects will
|
||||
be stored as separated files, NSUIntegerMax means all objects will be stored
|
||||
in sqlite. If you don't know your object's size, 20480 is a good choice.
|
||||
After first initialized you should not change this value of the specified path.
|
||||
|
||||
@return A new cache object, or nil if an error occurs.
|
||||
|
||||
@warning If the cache instance for the specified path already exists in memory,
|
||||
this method will return it directly, instead of creating a new instance.
|
||||
*/
|
||||
- (nullable instancetype)initWithPath:(NSString *)path
|
||||
inlineThreshold:(NSUInteger)threshold NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
#pragma mark - Access Methods
|
||||
///=============================================================================
|
||||
/// @name Access Methods
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Returns a boolean value that indicates whether a given key is in cache.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return NO.
|
||||
@return Whether the key is in cache.
|
||||
*/
|
||||
- (BOOL)containsObjectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns a boolean value with the block that indicates whether a given key is in cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return NO.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)containsObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key, BOOL contains))block;
|
||||
|
||||
/**
|
||||
Returns the value associated with a given key.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return nil.
|
||||
@return The value associated with key, or nil if no value is associated with key.
|
||||
*/
|
||||
- (nullable id<NSCoding>)objectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Returns the value associated with a given key.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key A string identifying the value. If nil, just return nil.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)objectForKey:(NSString *)key withBlock:(void(^)(NSString *key, id<NSCoding> _Nullable object))block;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache.
|
||||
This method may blocks the calling thread until file write finished.
|
||||
|
||||
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
|
||||
@param key The key with which to associate the value. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key withBlock:(void(^)(void))block;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
This method may blocks the calling thread until file delete finished.
|
||||
|
||||
@param key The key identifying the value to be removed. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)removeObjectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param key The key identifying the value to be removed. If nil, this method has no effect.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)removeObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key))block;
|
||||
|
||||
/**
|
||||
Empties the cache.
|
||||
This method may blocks the calling thread until file delete finished.
|
||||
*/
|
||||
- (void)removeAllObjects;
|
||||
|
||||
/**
|
||||
Empties the cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)removeAllObjectsWithBlock:(void(^)(void))block;
|
||||
|
||||
/**
|
||||
Empties the cache with block.
|
||||
This method returns immediately and executes the clear operation with block in background.
|
||||
|
||||
@warning You should not send message to this instance in these blocks.
|
||||
@param progress This block will be invoked during removing, pass nil to ignore.
|
||||
@param end This block will be invoked at the end, pass nil to ignore.
|
||||
*/
|
||||
- (void)removeAllObjectsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
|
||||
endBlock:(nullable void(^)(BOOL error))end;
|
||||
|
||||
|
||||
/**
|
||||
Returns the number of objects in this cache.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@return The total objects count.
|
||||
*/
|
||||
- (NSInteger)totalCount;
|
||||
|
||||
/**
|
||||
Get the number of objects in this cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)totalCountWithBlock:(void(^)(NSInteger totalCount))block;
|
||||
|
||||
/**
|
||||
Returns the total cost (in bytes) of objects in this cache.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@return The total objects cost in bytes.
|
||||
*/
|
||||
- (NSInteger)totalCost;
|
||||
|
||||
/**
|
||||
Get the total cost (in bytes) of objects in this cache.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)totalCostWithBlock:(void(^)(NSInteger totalCost))block;
|
||||
|
||||
|
||||
#pragma mark - Trim
|
||||
///=============================================================================
|
||||
/// @name Trim
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until the `totalCount` is below the specified value.
|
||||
This method may blocks the calling thread until operation finished.
|
||||
|
||||
@param count The total count allowed to remain after the cache has been trimmed.
|
||||
*/
|
||||
- (void)trimToCount:(NSUInteger)count;
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until the `totalCount` is below the specified value.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param count The total count allowed to remain after the cache has been trimmed.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)trimToCount:(NSUInteger)count withBlock:(void(^)(void))block;
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until the `totalCost` is below the specified value.
|
||||
This method may blocks the calling thread until operation finished.
|
||||
|
||||
@param cost The total cost allowed to remain after the cache has been trimmed.
|
||||
*/
|
||||
- (void)trimToCost:(NSUInteger)cost;
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until the `totalCost` is below the specified value.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param cost The total cost allowed to remain after the cache has been trimmed.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)trimToCost:(NSUInteger)cost withBlock:(void(^)(void))block;
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until all expiry objects removed by the specified value.
|
||||
This method may blocks the calling thread until operation finished.
|
||||
|
||||
@param age The maximum age of the object.
|
||||
*/
|
||||
- (void)trimToAge:(NSTimeInterval)age;
|
||||
|
||||
/**
|
||||
Removes objects from the cache use LRU, until all expiry objects removed by the specified value.
|
||||
This method returns immediately and invoke the passed block in background queue
|
||||
when the operation finished.
|
||||
|
||||
@param age The maximum age of the object.
|
||||
@param block A block which will be invoked in background queue when finished.
|
||||
*/
|
||||
- (void)trimToAge:(NSTimeInterval)age withBlock:(void(^)(void))block;
|
||||
|
||||
|
||||
#pragma mark - Extended Data
|
||||
///=============================================================================
|
||||
/// @name Extended Data
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Get extended data from an object.
|
||||
|
||||
@discussion See 'setExtendedData:toObject:' for more information.
|
||||
|
||||
@param object An object.
|
||||
@return The extended data.
|
||||
*/
|
||||
+ (nullable NSData *)getExtendedDataFromObject:(id)object;
|
||||
|
||||
/**
|
||||
Set extended data to an object.
|
||||
|
||||
@discussion You can set any extended data to an object before you save the object
|
||||
to disk cache. The extended data will also be saved with this object. You can get
|
||||
the extended data later with "getExtendedDataFromObject:".
|
||||
|
||||
@param extendedData The extended data (pass nil to remove).
|
||||
@param object The object.
|
||||
*/
|
||||
+ (void)setExtendedData:(nullable NSData *)extendedData toObject:(id)object;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,458 @@
|
|||
//
|
||||
// YYDiskCache.m
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/11.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYDiskCache.h"
|
||||
#import "YYKVStorage.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <time.h>
|
||||
|
||||
#define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
|
||||
#define Unlock() dispatch_semaphore_signal(self->_lock)
|
||||
|
||||
static const int extended_data_key;
|
||||
|
||||
/// Free disk space in bytes.
|
||||
static int64_t _YYDiskSpaceFree() {
|
||||
NSError *error = nil;
|
||||
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
|
||||
if (error) return -1;
|
||||
int64_t space = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
|
||||
if (space < 0) space = -1;
|
||||
return space;
|
||||
}
|
||||
|
||||
/// String's md5 hash.
|
||||
static NSString *_YYNSStringMD5(NSString *string) {
|
||||
if (!string) return nil;
|
||||
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(data.bytes, (CC_LONG)data.length, result);
|
||||
return [NSString stringWithFormat:
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
/// weak reference for all instances
|
||||
static NSMapTable *_globalInstances;
|
||||
static dispatch_semaphore_t _globalInstancesLock;
|
||||
|
||||
static void _YYDiskCacheInitGlobal() {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_globalInstancesLock = dispatch_semaphore_create(1);
|
||||
_globalInstances = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
|
||||
});
|
||||
}
|
||||
|
||||
static YYDiskCache *_YYDiskCacheGetGlobal(NSString *path) {
|
||||
if (path.length == 0) return nil;
|
||||
_YYDiskCacheInitGlobal();
|
||||
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
|
||||
id cache = [_globalInstances objectForKey:path];
|
||||
dispatch_semaphore_signal(_globalInstancesLock);
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
|
||||
if (cache.path.length == 0) return;
|
||||
_YYDiskCacheInitGlobal();
|
||||
dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
|
||||
[_globalInstances setObject:cache forKey:cache.path];
|
||||
dispatch_semaphore_signal(_globalInstancesLock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation YYDiskCache {
|
||||
YYKVStorage *_kv;
|
||||
dispatch_semaphore_t _lock;
|
||||
dispatch_queue_t _queue;
|
||||
}
|
||||
|
||||
- (void)_trimRecursively {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
[self _trimInBackground];
|
||||
[self _trimRecursively];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_trimInBackground {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
Lock();
|
||||
[self _trimToCost:self.costLimit];
|
||||
[self _trimToCount:self.countLimit];
|
||||
[self _trimToAge:self.ageLimit];
|
||||
[self _trimToFreeDiskSpace:self.freeDiskSpaceLimit];
|
||||
Unlock();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_trimToCost:(NSUInteger)costLimit {
|
||||
if (costLimit >= INT_MAX) return;
|
||||
[_kv removeItemsToFitSize:(int)costLimit];
|
||||
|
||||
}
|
||||
|
||||
- (void)_trimToCount:(NSUInteger)countLimit {
|
||||
if (countLimit >= INT_MAX) return;
|
||||
[_kv removeItemsToFitCount:(int)countLimit];
|
||||
}
|
||||
|
||||
- (void)_trimToAge:(NSTimeInterval)ageLimit {
|
||||
if (ageLimit <= 0) {
|
||||
[_kv removeAllItems];
|
||||
return;
|
||||
}
|
||||
long timestamp = time(NULL);
|
||||
if (timestamp <= ageLimit) return;
|
||||
long age = timestamp - ageLimit;
|
||||
if (age >= INT_MAX) return;
|
||||
[_kv removeItemsEarlierThanTime:(int)age];
|
||||
}
|
||||
|
||||
- (void)_trimToFreeDiskSpace:(NSUInteger)targetFreeDiskSpace {
|
||||
if (targetFreeDiskSpace == 0) return;
|
||||
int64_t totalBytes = [_kv getItemsSize];
|
||||
if (totalBytes <= 0) return;
|
||||
int64_t diskFreeBytes = _YYDiskSpaceFree();
|
||||
if (diskFreeBytes < 0) return;
|
||||
int64_t needTrimBytes = targetFreeDiskSpace - diskFreeBytes;
|
||||
if (needTrimBytes <= 0) return;
|
||||
int64_t costLimit = totalBytes - needTrimBytes;
|
||||
if (costLimit < 0) costLimit = 0;
|
||||
[self _trimToCost:(int)costLimit];
|
||||
}
|
||||
|
||||
- (NSString *)_filenameForKey:(NSString *)key {
|
||||
NSString *filename = nil;
|
||||
if (_customFileNameBlock) filename = _customFileNameBlock(key);
|
||||
if (!filename) filename = _YYNSStringMD5(key);
|
||||
return filename;
|
||||
}
|
||||
|
||||
- (void)_appWillBeTerminated {
|
||||
Lock();
|
||||
_kv = nil;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@throw [NSException exceptionWithName:@"YYDiskCache init error" reason:@"YYDiskCache must be initialized with a path. Use 'initWithPath:' or 'initWithPath:inlineThreshold:' instead." userInfo:nil];
|
||||
return [self initWithPath:@"" inlineThreshold:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path {
|
||||
return [self initWithPath:path inlineThreshold:1024 * 20]; // 20KB
|
||||
}
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path
|
||||
inlineThreshold:(NSUInteger)threshold {
|
||||
self = [super init];
|
||||
if (!self) return nil;
|
||||
|
||||
YYDiskCache *globalCache = _YYDiskCacheGetGlobal(path);
|
||||
if (globalCache) return globalCache;
|
||||
|
||||
YYKVStorageType type;
|
||||
if (threshold == 0) {
|
||||
type = YYKVStorageTypeFile;
|
||||
} else if (threshold == NSUIntegerMax) {
|
||||
type = YYKVStorageTypeSQLite;
|
||||
} else {
|
||||
type = YYKVStorageTypeMixed;
|
||||
}
|
||||
|
||||
YYKVStorage *kv = [[YYKVStorage alloc] initWithPath:path type:type];
|
||||
if (!kv) return nil;
|
||||
|
||||
_kv = kv;
|
||||
_path = path;
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
_queue = dispatch_queue_create("com.ibireme.cache.disk", DISPATCH_QUEUE_CONCURRENT);
|
||||
_inlineThreshold = threshold;
|
||||
_countLimit = NSUIntegerMax;
|
||||
_costLimit = NSUIntegerMax;
|
||||
_ageLimit = DBL_MAX;
|
||||
_freeDiskSpaceLimit = 0;
|
||||
_autoTrimInterval = 60;
|
||||
|
||||
[self _trimRecursively];
|
||||
_YYDiskCacheSetGlobal(self);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appWillBeTerminated) name:UIApplicationWillTerminateNotification object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)containsObjectForKey:(NSString *)key {
|
||||
if (!key) return NO;
|
||||
Lock();
|
||||
BOOL contains = [_kv itemExistsForKey:key];
|
||||
Unlock();
|
||||
return contains;
|
||||
}
|
||||
|
||||
- (void)containsObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key, BOOL contains))block {
|
||||
if (!block) return;
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
BOOL contains = [self containsObjectForKey:key];
|
||||
block(key, contains);
|
||||
});
|
||||
}
|
||||
|
||||
- (id<NSCoding>)objectForKey:(NSString *)key {
|
||||
if (!key) return nil;
|
||||
Lock();
|
||||
YYKVStorageItem *item = [_kv getItemForKey:key];
|
||||
Unlock();
|
||||
if (!item.value) return nil;
|
||||
|
||||
id object = nil;
|
||||
if (_customUnarchiveBlock) {
|
||||
object = _customUnarchiveBlock(item.value);
|
||||
} else {
|
||||
@try {
|
||||
object = [NSKeyedUnarchiver unarchiveObjectWithData:item.value];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
// nothing to do...
|
||||
}
|
||||
}
|
||||
if (object && item.extendedData) {
|
||||
[YYDiskCache setExtendedData:item.extendedData toObject:object];
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void)objectForKey:(NSString *)key withBlock:(void(^)(NSString *key, id<NSCoding> object))block {
|
||||
if (!block) return;
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
id<NSCoding> object = [self objectForKey:key];
|
||||
block(key, object);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key {
|
||||
if (!key) return;
|
||||
if (!object) {
|
||||
[self removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *extendedData = [YYDiskCache getExtendedDataFromObject:object];
|
||||
NSData *value = nil;
|
||||
if (_customArchiveBlock) {
|
||||
value = _customArchiveBlock(object);
|
||||
} else {
|
||||
@try {
|
||||
value = [NSKeyedArchiver archivedDataWithRootObject:object];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
// nothing to do...
|
||||
}
|
||||
}
|
||||
if (!value) return;
|
||||
NSString *filename = nil;
|
||||
if (_kv.type != YYKVStorageTypeSQLite) {
|
||||
if (value.length > _inlineThreshold) {
|
||||
filename = [self _filenameForKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
Lock();
|
||||
[_kv saveItemWithKey:key value:value filename:filename extendedData:extendedData];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key withBlock:(void(^)(void))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self setObject:object forKey:key];
|
||||
if (block) block();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key {
|
||||
if (!key) return;
|
||||
Lock();
|
||||
[_kv removeItemForKey:key];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key withBlock:(void(^)(NSString *key))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self removeObjectForKey:key];
|
||||
if (block) block(key);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
Lock();
|
||||
[_kv removeAllItems];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)removeAllObjectsWithBlock:(void(^)(void))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self removeAllObjects];
|
||||
if (block) block();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllObjectsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress
|
||||
endBlock:(void(^)(BOOL error))end {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) {
|
||||
if (end) end(YES);
|
||||
return;
|
||||
}
|
||||
Lock();
|
||||
[_kv removeAllItemsWithProgressBlock:progress endBlock:end];
|
||||
Unlock();
|
||||
});
|
||||
}
|
||||
|
||||
- (NSInteger)totalCount {
|
||||
Lock();
|
||||
int count = [_kv getItemsCount];
|
||||
Unlock();
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)totalCountWithBlock:(void(^)(NSInteger totalCount))block {
|
||||
if (!block) return;
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
NSInteger totalCount = [self totalCount];
|
||||
block(totalCount);
|
||||
});
|
||||
}
|
||||
|
||||
- (NSInteger)totalCost {
|
||||
Lock();
|
||||
int count = [_kv getItemsSize];
|
||||
Unlock();
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)totalCostWithBlock:(void(^)(NSInteger totalCost))block {
|
||||
if (!block) return;
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
NSInteger totalCost = [self totalCost];
|
||||
block(totalCost);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)trimToCount:(NSUInteger)count {
|
||||
Lock();
|
||||
[self _trimToCount:count];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)trimToCount:(NSUInteger)count withBlock:(void(^)(void))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self trimToCount:count];
|
||||
if (block) block();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)trimToCost:(NSUInteger)cost {
|
||||
Lock();
|
||||
[self _trimToCost:cost];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)trimToCost:(NSUInteger)cost withBlock:(void(^)(void))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self trimToCost:cost];
|
||||
if (block) block();
|
||||
});
|
||||
}
|
||||
|
||||
- (void)trimToAge:(NSTimeInterval)age {
|
||||
Lock();
|
||||
[self _trimToAge:age];
|
||||
Unlock();
|
||||
}
|
||||
|
||||
- (void)trimToAge:(NSTimeInterval)age withBlock:(void(^)(void))block {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_async(_queue, ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
[self trimToAge:age];
|
||||
if (block) block();
|
||||
});
|
||||
}
|
||||
|
||||
+ (NSData *)getExtendedDataFromObject:(id)object {
|
||||
if (!object) return nil;
|
||||
return (NSData *)objc_getAssociatedObject(object, &extended_data_key);
|
||||
}
|
||||
|
||||
+ (void)setExtendedData:(NSData *)extendedData toObject:(id)object {
|
||||
if (!object) return;
|
||||
objc_setAssociatedObject(object, &extended_data_key, extendedData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@:%@)", self.class, self, _name, _path];
|
||||
else return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _path];
|
||||
}
|
||||
|
||||
- (BOOL)errorLogsEnabled {
|
||||
Lock();
|
||||
BOOL enabled = _kv.errorLogsEnabled;
|
||||
Unlock();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
- (void)setErrorLogsEnabled:(BOOL)errorLogsEnabled {
|
||||
Lock();
|
||||
_kv.errorLogsEnabled = errorLogsEnabled;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,325 @@
|
|||
//
|
||||
// YYKVStorage.h
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/4/22.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
YYKVStorageItem is used by `YYKVStorage` to store key-value pair and meta data.
|
||||
Typically, you should not use this class directly.
|
||||
*/
|
||||
@interface YYKVStorageItem : NSObject
|
||||
@property (nonatomic, strong) NSString *key; ///< key
|
||||
@property (nonatomic, strong) NSData *value; ///< value
|
||||
@property (nullable, nonatomic, strong) NSString *filename; ///< filename (nil if inline)
|
||||
@property (nonatomic) int size; ///< value's size in bytes
|
||||
@property (nonatomic) int modTime; ///< modification unix timestamp
|
||||
@property (nonatomic) int accessTime; ///< last access unix timestamp
|
||||
@property (nullable, nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
|
||||
@end
|
||||
|
||||
/**
|
||||
Storage type, indicated where the `YYKVStorageItem.value` stored.
|
||||
|
||||
@discussion Typically, write data to sqlite is faster than extern file, but
|
||||
reading performance is dependent on data size. In my test (on iPhone 6 64G),
|
||||
read data from extern file is faster than from sqlite when the data is larger
|
||||
than 20KB.
|
||||
|
||||
* If you want to store large number of small datas (such as contacts cache),
|
||||
use YYKVStorageTypeSQLite to get better performance.
|
||||
* If you want to store large files (such as image cache),
|
||||
use YYKVStorageTypeFile to get better performance.
|
||||
* You can use YYKVStorageTypeMixed and choice your storage type for each item.
|
||||
|
||||
See <http://www.sqlite.org/intern-v-extern-blob.html> for more information.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, YYKVStorageType) {
|
||||
|
||||
/// The `value` is stored as a file in file system.
|
||||
YYKVStorageTypeFile = 0,
|
||||
|
||||
/// The `value` is stored in sqlite with blob type.
|
||||
YYKVStorageTypeSQLite = 1,
|
||||
|
||||
/// The `value` is stored in file system or sqlite based on your choice.
|
||||
YYKVStorageTypeMixed = 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
YYKVStorage is a key-value storage based on sqlite and file system.
|
||||
Typically, you should not use this class directly.
|
||||
|
||||
@discussion The designated initializer for YYKVStorage is `initWithPath:type:`.
|
||||
After initialized, a directory is created based on the `path` to hold key-value data.
|
||||
Once initialized you should not read or write this directory without the instance.
|
||||
|
||||
You may compile the latest version of sqlite and ignore the libsqlite3.dylib in
|
||||
iOS system to get 2x~4x speed up.
|
||||
|
||||
@warning The instance of this class is *NOT* thread safe, you need to make sure
|
||||
that there's only one thread to access the instance at the same time. If you really
|
||||
need to process large amounts of data in multi-thread, you should split the data
|
||||
to multiple KVStorage instance (sharding).
|
||||
*/
|
||||
@interface YYKVStorage : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
|
||||
@property (nonatomic, readonly) NSString *path; ///< The path of this storage.
|
||||
@property (nonatomic, readonly) YYKVStorageType type; ///< The type of this storage.
|
||||
@property (nonatomic) BOOL errorLogsEnabled; ///< Set `YES` to enable error logs for debug.
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
/// @name Initializer
|
||||
///=============================================================================
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer.
|
||||
|
||||
@param path Full path of a directory in which the storage will write data. If
|
||||
the directory is not exists, it will try to create one, otherwise it will
|
||||
read the data in this directory.
|
||||
@param type The storage type. After first initialized you should not change the
|
||||
type of the specified path.
|
||||
@return A new storage object, or nil if an error occurs.
|
||||
@warning Multiple instances with the same path will make the storage unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithPath:(NSString *)path type:(YYKVStorageType)type NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
#pragma mark - Save Items
|
||||
///=============================================================================
|
||||
/// @name Save Items
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Save an item or update the item with 'key' if it already exists.
|
||||
|
||||
@discussion This method will save the item.key, item.value, item.filename and
|
||||
item.extendedData to disk or sqlite, other properties will be ignored. item.key
|
||||
and item.value should not be empty (nil or zero length).
|
||||
|
||||
If the `type` is YYKVStorageTypeFile, then the item.filename should not be empty.
|
||||
If the `type` is YYKVStorageTypeSQLite, then the item.filename will be ignored.
|
||||
It the `type` is YYKVStorageTypeMixed, then the item.value will be saved to file
|
||||
system if the item.filename is not empty, otherwise it will be saved to sqlite.
|
||||
|
||||
@param item An item.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)saveItem:(YYKVStorageItem *)item;
|
||||
|
||||
/**
|
||||
Save an item or update the item with 'key' if it already exists.
|
||||
|
||||
@discussion This method will save the key-value pair to sqlite. If the `type` is
|
||||
YYKVStorageTypeFile, then this method will failed.
|
||||
|
||||
@param key The key, should not be empty (nil or zero length).
|
||||
@param value The key, should not be empty (nil or zero length).
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value;
|
||||
|
||||
/**
|
||||
Save an item or update the item with 'key' if it already exists.
|
||||
|
||||
@discussion
|
||||
If the `type` is YYKVStorageTypeFile, then the `filename` should not be empty.
|
||||
If the `type` is YYKVStorageTypeSQLite, then the `filename` will be ignored.
|
||||
It the `type` is YYKVStorageTypeMixed, then the `value` will be saved to file
|
||||
system if the `filename` is not empty, otherwise it will be saved to sqlite.
|
||||
|
||||
@param key The key, should not be empty (nil or zero length).
|
||||
@param value The key, should not be empty (nil or zero length).
|
||||
@param filename The filename.
|
||||
@param extendedData The extended data for this item (pass nil to ignore it).
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)saveItemWithKey:(NSString *)key
|
||||
value:(NSData *)value
|
||||
filename:(nullable NSString *)filename
|
||||
extendedData:(nullable NSData *)extendedData;
|
||||
|
||||
#pragma mark - Remove Items
|
||||
///=============================================================================
|
||||
/// @name Remove Items
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Remove an item with 'key'.
|
||||
|
||||
@param key The item's key.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Remove items with an array of keys.
|
||||
|
||||
@param keys An array of specified keys.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemForKeys:(NSArray<NSString *> *)keys;
|
||||
|
||||
/**
|
||||
Remove all items which `value` is larger than a specified size.
|
||||
|
||||
@param size The maximum size in bytes.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemsLargerThanSize:(int)size;
|
||||
|
||||
/**
|
||||
Remove all items which last access time is earlier than a specified timestamp.
|
||||
|
||||
@param time The specified unix timestamp.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemsEarlierThanTime:(int)time;
|
||||
|
||||
/**
|
||||
Remove items to make the total size not larger than a specified size.
|
||||
The least recently used (LRU) items will be removed first.
|
||||
|
||||
@param maxSize The specified size in bytes.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemsToFitSize:(int)maxSize;
|
||||
|
||||
/**
|
||||
Remove items to make the total count not larger than a specified count.
|
||||
The least recently used (LRU) items will be removed first.
|
||||
|
||||
@param maxCount The specified item count.
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeItemsToFitCount:(int)maxCount;
|
||||
|
||||
/**
|
||||
Remove all items in background queue.
|
||||
|
||||
@discussion This method will remove the files and sqlite database to a trash
|
||||
folder, and then clear the folder in background queue. So this method is much
|
||||
faster than `removeAllItemsWithProgressBlock:endBlock:`.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)removeAllItems;
|
||||
|
||||
/**
|
||||
Remove all items.
|
||||
|
||||
@warning You should not send message to this instance in these blocks.
|
||||
@param progress This block will be invoked during removing, pass nil to ignore.
|
||||
@param end This block will be invoked at the end, pass nil to ignore.
|
||||
*/
|
||||
- (void)removeAllItemsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
|
||||
endBlock:(nullable void(^)(BOOL error))end;
|
||||
|
||||
|
||||
#pragma mark - Get Items
|
||||
///=============================================================================
|
||||
/// @name Get Items
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Get item with a specified key.
|
||||
|
||||
@param key A specified key.
|
||||
@return Item for the key, or nil if not exists / error occurs.
|
||||
*/
|
||||
- (nullable YYKVStorageItem *)getItemForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Get item information with a specified key.
|
||||
The `value` in this item will be ignored.
|
||||
|
||||
@param key A specified key.
|
||||
@return Item information for the key, or nil if not exists / error occurs.
|
||||
*/
|
||||
- (nullable YYKVStorageItem *)getItemInfoForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Get item value with a specified key.
|
||||
|
||||
@param key A specified key.
|
||||
@return Item's value, or nil if not exists / error occurs.
|
||||
*/
|
||||
- (nullable NSData *)getItemValueForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Get items with an array of keys.
|
||||
|
||||
@param keys An array of specified keys.
|
||||
@return An array of `YYKVStorageItem`, or nil if not exists / error occurs.
|
||||
*/
|
||||
- (nullable NSArray<YYKVStorageItem *> *)getItemForKeys:(NSArray<NSString *> *)keys;
|
||||
|
||||
/**
|
||||
Get item infomartions with an array of keys.
|
||||
The `value` in items will be ignored.
|
||||
|
||||
@param keys An array of specified keys.
|
||||
@return An array of `YYKVStorageItem`, or nil if not exists / error occurs.
|
||||
*/
|
||||
- (nullable NSArray<YYKVStorageItem *> *)getItemInfoForKeys:(NSArray<NSString *> *)keys;
|
||||
|
||||
/**
|
||||
Get items value with an array of keys.
|
||||
|
||||
@param keys An array of specified keys.
|
||||
@return A dictionary which key is 'key' and value is 'value', or nil if not
|
||||
exists / error occurs.
|
||||
*/
|
||||
- (nullable NSDictionary<NSString *, NSData *> *)getItemValueForKeys:(NSArray<NSString *> *)keys;
|
||||
|
||||
#pragma mark - Get Storage Status
|
||||
///=============================================================================
|
||||
/// @name Get Storage Status
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Whether an item exists for a specified key.
|
||||
|
||||
@param key A specified key.
|
||||
|
||||
@return `YES` if there's an item exists for the key, `NO` if not exists or an error occurs.
|
||||
*/
|
||||
- (BOOL)itemExistsForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
Get total item count.
|
||||
@return Total item count, -1 when an error occurs.
|
||||
*/
|
||||
- (int)getItemsCount;
|
||||
|
||||
/**
|
||||
Get item value's total size in bytes.
|
||||
@return Total size in bytes, -1 when an error occurs.
|
||||
*/
|
||||
- (int)getItemsSize;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,213 @@
|
|||
//
|
||||
// YYMemoryCache.h
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/7.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
YYMemoryCache is a fast in-memory cache that stores key-value pairs.
|
||||
In contrast to NSDictionary, keys are retained and not copied.
|
||||
The API and performance is similar to `NSCache`, all methods are thread-safe.
|
||||
|
||||
YYMemoryCache objects differ from NSCache in a few ways:
|
||||
|
||||
* It uses LRU (least-recently-used) to remove objects; NSCache's eviction method
|
||||
is non-deterministic.
|
||||
* It can be controlled by cost, count and age; NSCache's limits are imprecise.
|
||||
* It can be configured to automatically evict objects when receive memory
|
||||
warning or app enter background.
|
||||
|
||||
The time of `Access Methods` in YYMemoryCache is typically in constant time (O(1)).
|
||||
*/
|
||||
@interface YYMemoryCache : NSObject
|
||||
|
||||
#pragma mark - Attribute
|
||||
///=============================================================================
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
|
||||
/** The name of the cache. Default is nil. */
|
||||
@property (nullable, copy) NSString *name;
|
||||
|
||||
/** The number of objects in the cache (read-only) */
|
||||
@property (readonly) NSUInteger totalCount;
|
||||
|
||||
/** The total cost of objects in the cache (read-only). */
|
||||
@property (readonly) NSUInteger totalCost;
|
||||
|
||||
|
||||
#pragma mark - Limit
|
||||
///=============================================================================
|
||||
/// @name Limit
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
The maximum number of objects the cache should hold.
|
||||
|
||||
@discussion The default value is NSUIntegerMax, which means no limit.
|
||||
This is not a strict limit—if the cache goes over the limit, some objects in the
|
||||
cache could be evicted later in backgound thread.
|
||||
*/
|
||||
@property NSUInteger countLimit;
|
||||
|
||||
/**
|
||||
The maximum total cost that the cache can hold before it starts evicting objects.
|
||||
|
||||
@discussion The default value is NSUIntegerMax, which means no limit.
|
||||
This is not a strict limit—if the cache goes over the limit, some objects in the
|
||||
cache could be evicted later in backgound thread.
|
||||
*/
|
||||
@property NSUInteger costLimit;
|
||||
|
||||
/**
|
||||
The maximum expiry time of objects in cache.
|
||||
|
||||
@discussion The default value is DBL_MAX, which means no limit.
|
||||
This is not a strict limit—if an object goes over the limit, the object could
|
||||
be evicted later in backgound thread.
|
||||
*/
|
||||
@property NSTimeInterval ageLimit;
|
||||
|
||||
/**
|
||||
The auto trim check time interval in seconds. Default is 5.0.
|
||||
|
||||
@discussion The cache holds an internal timer to check whether the cache reaches
|
||||
its limits, and if the limit is reached, it begins to evict objects.
|
||||
*/
|
||||
@property NSTimeInterval autoTrimInterval;
|
||||
|
||||
/**
|
||||
If `YES`, the cache will remove all objects when the app receives a memory warning.
|
||||
The default value is `YES`.
|
||||
*/
|
||||
@property BOOL shouldRemoveAllObjectsOnMemoryWarning;
|
||||
|
||||
/**
|
||||
If `YES`, The cache will remove all objects when the app enter background.
|
||||
The default value is `YES`.
|
||||
*/
|
||||
@property BOOL shouldRemoveAllObjectsWhenEnteringBackground;
|
||||
|
||||
/**
|
||||
A block to be executed when the app receives a memory warning.
|
||||
The default value is nil.
|
||||
*/
|
||||
@property (nullable, copy) void(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache);
|
||||
|
||||
/**
|
||||
A block to be executed when the app enter background.
|
||||
The default value is nil.
|
||||
*/
|
||||
@property (nullable, copy) void(^didEnterBackgroundBlock)(YYMemoryCache *cache);
|
||||
|
||||
/**
|
||||
If `YES`, the key-value pair will be released on main thread, otherwise on
|
||||
background thread. Default is NO.
|
||||
|
||||
@discussion You may set this value to `YES` if the key-value object contains
|
||||
the instance which should be released in main thread (such as UIView/CALayer).
|
||||
*/
|
||||
@property BOOL releaseOnMainThread;
|
||||
|
||||
/**
|
||||
If `YES`, the key-value pair will be released asynchronously to avoid blocking
|
||||
the access methods, otherwise it will be released in the access method
|
||||
(such as removeObjectForKey:). Default is YES.
|
||||
*/
|
||||
@property BOOL releaseAsynchronously;
|
||||
|
||||
|
||||
#pragma mark - Access Methods
|
||||
///=============================================================================
|
||||
/// @name Access Methods
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Returns a Boolean value that indicates whether a given key is in cache.
|
||||
|
||||
@param key An object identifying the value. If nil, just return `NO`.
|
||||
@return Whether the key is in cache.
|
||||
*/
|
||||
- (BOOL)containsObjectForKey:(id)key;
|
||||
|
||||
/**
|
||||
Returns the value associated with a given key.
|
||||
|
||||
@param key An object identifying the value. If nil, just return nil.
|
||||
@return The value associated with key, or nil if no value is associated with key.
|
||||
*/
|
||||
- (nullable id)objectForKey:(id)key;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache (0 cost).
|
||||
|
||||
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
|
||||
@param key The key with which to associate the value. If nil, this method has no effect.
|
||||
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
|
||||
objects that are put into it.
|
||||
*/
|
||||
- (void)setObject:(nullable id)object forKey:(id)key;
|
||||
|
||||
/**
|
||||
Sets the value of the specified key in the cache, and associates the key-value
|
||||
pair with the specified cost.
|
||||
|
||||
@param object The object to store in the cache. If nil, it calls `removeObjectForKey`.
|
||||
@param key The key with which to associate the value. If nil, this method has no effect.
|
||||
@param cost The cost with which to associate the key-value pair.
|
||||
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
|
||||
objects that are put into it.
|
||||
*/
|
||||
- (void)setObject:(nullable id)object forKey:(id)key withCost:(NSUInteger)cost;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
|
||||
@param key The key identifying the value to be removed. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)removeObjectForKey:(id)key;
|
||||
|
||||
/**
|
||||
Empties the cache immediately.
|
||||
*/
|
||||
- (void)removeAllObjects;
|
||||
|
||||
|
||||
#pragma mark - Trim
|
||||
///=============================================================================
|
||||
/// @name Trim
|
||||
///=============================================================================
|
||||
|
||||
/**
|
||||
Removes objects from the cache with LRU, until the `totalCount` is below or equal to
|
||||
the specified value.
|
||||
@param count The total count allowed to remain after the cache has been trimmed.
|
||||
*/
|
||||
- (void)trimToCount:(NSUInteger)count;
|
||||
|
||||
/**
|
||||
Removes objects from the cache with LRU, until the `totalCost` is or equal to
|
||||
the specified value.
|
||||
@param cost The total cost allowed to remain after the cache has been trimmed.
|
||||
*/
|
||||
- (void)trimToCost:(NSUInteger)cost;
|
||||
|
||||
/**
|
||||
Removes objects from the cache with LRU, until all expiry objects removed by the
|
||||
specified value.
|
||||
@param age The maximum age (in seconds) of objects.
|
||||
*/
|
||||
- (void)trimToAge:(NSTimeInterval)age;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,505 @@
|
|||
//
|
||||
// YYMemoryCache.m
|
||||
// YYCache <https://github.com/ibireme/YYCache>
|
||||
//
|
||||
// Created by ibireme on 15/2/7.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYMemoryCache.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <pthread.h>
|
||||
|
||||
|
||||
static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
|
||||
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
A node in linked map.
|
||||
Typically, you should not use this class directly.
|
||||
*/
|
||||
@interface _YYLinkedMapNode : NSObject {
|
||||
@package
|
||||
__unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic
|
||||
__unsafe_unretained _YYLinkedMapNode *_next; // retained by dic
|
||||
id _key;
|
||||
id _value;
|
||||
NSUInteger _cost;
|
||||
NSTimeInterval _time;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation _YYLinkedMapNode
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
A linked map used by YYMemoryCache.
|
||||
It's not thread-safe and does not validate the parameters.
|
||||
|
||||
Typically, you should not use this class directly.
|
||||
*/
|
||||
@interface _YYLinkedMap : NSObject {
|
||||
@package
|
||||
CFMutableDictionaryRef _dic; // do not set object directly
|
||||
NSUInteger _totalCost;
|
||||
NSUInteger _totalCount;
|
||||
_YYLinkedMapNode *_head; // MRU, do not change it directly
|
||||
_YYLinkedMapNode *_tail; // LRU, do not change it directly
|
||||
BOOL _releaseOnMainThread;
|
||||
BOOL _releaseAsynchronously;
|
||||
}
|
||||
|
||||
/// Insert a node at head and update the total cost.
|
||||
/// Node and node.key should not be nil.
|
||||
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
|
||||
|
||||
/// Bring a inner node to header.
|
||||
/// Node should already inside the dic.
|
||||
- (void)bringNodeToHead:(_YYLinkedMapNode *)node;
|
||||
|
||||
/// Remove a inner node and update the total cost.
|
||||
/// Node should already inside the dic.
|
||||
- (void)removeNode:(_YYLinkedMapNode *)node;
|
||||
|
||||
/// Remove tail node if exist.
|
||||
- (_YYLinkedMapNode *)removeTailNode;
|
||||
|
||||
/// Remove all node in background queue.
|
||||
- (void)removeAll;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _YYLinkedMap
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
_releaseOnMainThread = NO;
|
||||
_releaseAsynchronously = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
CFRelease(_dic);
|
||||
}
|
||||
|
||||
- (void)insertNodeAtHead:(_YYLinkedMapNode *)node {
|
||||
CFDictionarySetValue(_dic, (__bridge const void *)(node->_key), (__bridge const void *)(node));
|
||||
_totalCost += node->_cost;
|
||||
_totalCount++;
|
||||
if (_head) {
|
||||
node->_next = _head;
|
||||
_head->_prev = node;
|
||||
_head = node;
|
||||
} else {
|
||||
_head = _tail = node;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)bringNodeToHead:(_YYLinkedMapNode *)node {
|
||||
if (_head == node) return;
|
||||
|
||||
if (_tail == node) {
|
||||
_tail = node->_prev;
|
||||
_tail->_next = nil;
|
||||
} else {
|
||||
node->_next->_prev = node->_prev;
|
||||
node->_prev->_next = node->_next;
|
||||
}
|
||||
node->_next = _head;
|
||||
node->_prev = nil;
|
||||
_head->_prev = node;
|
||||
_head = node;
|
||||
}
|
||||
|
||||
- (void)removeNode:(_YYLinkedMapNode *)node {
|
||||
CFDictionaryRemoveValue(_dic, (__bridge const void *)(node->_key));
|
||||
_totalCost -= node->_cost;
|
||||
_totalCount--;
|
||||
if (node->_next) node->_next->_prev = node->_prev;
|
||||
if (node->_prev) node->_prev->_next = node->_next;
|
||||
if (_head == node) _head = node->_next;
|
||||
if (_tail == node) _tail = node->_prev;
|
||||
}
|
||||
|
||||
- (_YYLinkedMapNode *)removeTailNode {
|
||||
if (!_tail) return nil;
|
||||
_YYLinkedMapNode *tail = _tail;
|
||||
CFDictionaryRemoveValue(_dic, (__bridge const void *)(_tail->_key));
|
||||
_totalCost -= _tail->_cost;
|
||||
_totalCount--;
|
||||
if (_head == _tail) {
|
||||
_head = _tail = nil;
|
||||
} else {
|
||||
_tail = _tail->_prev;
|
||||
_tail->_next = nil;
|
||||
}
|
||||
return tail;
|
||||
}
|
||||
|
||||
- (void)removeAll {
|
||||
_totalCost = 0;
|
||||
_totalCount = 0;
|
||||
_head = nil;
|
||||
_tail = nil;
|
||||
if (CFDictionaryGetCount(_dic) > 0) {
|
||||
CFMutableDictionaryRef holder = _dic;
|
||||
_dic = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (_releaseAsynchronously) {
|
||||
dispatch_queue_t queue = _releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
CFRelease(holder); // hold and release in specified queue
|
||||
});
|
||||
} else if (_releaseOnMainThread && !pthread_main_np()) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
CFRelease(holder); // hold and release in specified queue
|
||||
});
|
||||
} else {
|
||||
CFRelease(holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation YYMemoryCache {
|
||||
pthread_mutex_t _lock;
|
||||
_YYLinkedMap *_lru;
|
||||
dispatch_queue_t _queue;
|
||||
}
|
||||
|
||||
- (void)_trimRecursively {
|
||||
__weak typeof(self) _self = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
__strong typeof(_self) self = _self;
|
||||
if (!self) return;
|
||||
[self _trimInBackground];
|
||||
[self _trimRecursively];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_trimInBackground {
|
||||
dispatch_async(_queue, ^{
|
||||
[self _trimToCost:self->_costLimit];
|
||||
[self _trimToCount:self->_countLimit];
|
||||
[self _trimToAge:self->_ageLimit];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_trimToCost:(NSUInteger)costLimit {
|
||||
BOOL finish = NO;
|
||||
pthread_mutex_lock(&_lock);
|
||||
if (costLimit == 0) {
|
||||
[_lru removeAll];
|
||||
finish = YES;
|
||||
} else if (_lru->_totalCost <= costLimit) {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
if (finish) return;
|
||||
|
||||
NSMutableArray *holder = [NSMutableArray new];
|
||||
while (!finish) {
|
||||
if (pthread_mutex_trylock(&_lock) == 0) {
|
||||
if (_lru->_totalCost > costLimit) {
|
||||
_YYLinkedMapNode *node = [_lru removeTailNode];
|
||||
if (node) [holder addObject:node];
|
||||
} else {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
} else {
|
||||
usleep(10 * 1000); //10 ms
|
||||
}
|
||||
}
|
||||
if (holder.count) {
|
||||
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
[holder count]; // release in queue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_trimToCount:(NSUInteger)countLimit {
|
||||
BOOL finish = NO;
|
||||
pthread_mutex_lock(&_lock);
|
||||
if (countLimit == 0) {
|
||||
[_lru removeAll];
|
||||
finish = YES;
|
||||
} else if (_lru->_totalCount <= countLimit) {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
if (finish) return;
|
||||
|
||||
NSMutableArray *holder = [NSMutableArray new];
|
||||
while (!finish) {
|
||||
if (pthread_mutex_trylock(&_lock) == 0) {
|
||||
if (_lru->_totalCount > countLimit) {
|
||||
_YYLinkedMapNode *node = [_lru removeTailNode];
|
||||
if (node) [holder addObject:node];
|
||||
} else {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
} else {
|
||||
usleep(10 * 1000); //10 ms
|
||||
}
|
||||
}
|
||||
if (holder.count) {
|
||||
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
[holder count]; // release in queue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_trimToAge:(NSTimeInterval)ageLimit {
|
||||
BOOL finish = NO;
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
pthread_mutex_lock(&_lock);
|
||||
if (ageLimit <= 0) {
|
||||
[_lru removeAll];
|
||||
finish = YES;
|
||||
} else if (!_lru->_tail || (now - _lru->_tail->_time) <= ageLimit) {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
if (finish) return;
|
||||
|
||||
NSMutableArray *holder = [NSMutableArray new];
|
||||
while (!finish) {
|
||||
if (pthread_mutex_trylock(&_lock) == 0) {
|
||||
if (_lru->_tail && (now - _lru->_tail->_time) > ageLimit) {
|
||||
_YYLinkedMapNode *node = [_lru removeTailNode];
|
||||
if (node) [holder addObject:node];
|
||||
} else {
|
||||
finish = YES;
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
} else {
|
||||
usleep(10 * 1000); //10 ms
|
||||
}
|
||||
}
|
||||
if (holder.count) {
|
||||
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
[holder count]; // release in queue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_appDidReceiveMemoryWarningNotification {
|
||||
if (self.didReceiveMemoryWarningBlock) {
|
||||
self.didReceiveMemoryWarningBlock(self);
|
||||
}
|
||||
if (self.shouldRemoveAllObjectsOnMemoryWarning) {
|
||||
[self removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_appDidEnterBackgroundNotification {
|
||||
if (self.didEnterBackgroundBlock) {
|
||||
self.didEnterBackgroundBlock(self);
|
||||
}
|
||||
if (self.shouldRemoveAllObjectsWhenEnteringBackground) {
|
||||
[self removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
- (instancetype)init {
|
||||
self = super.init;
|
||||
pthread_mutex_init(&_lock, NULL);
|
||||
_lru = [_YYLinkedMap new];
|
||||
_queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
_countLimit = NSUIntegerMax;
|
||||
_costLimit = NSUIntegerMax;
|
||||
_ageLimit = DBL_MAX;
|
||||
_autoTrimInterval = 5.0;
|
||||
_shouldRemoveAllObjectsOnMemoryWarning = YES;
|
||||
_shouldRemoveAllObjectsWhenEnteringBackground = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
|
||||
[self _trimRecursively];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[_lru removeAll];
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
- (NSUInteger)totalCount {
|
||||
pthread_mutex_lock(&_lock);
|
||||
NSUInteger count = _lru->_totalCount;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSUInteger)totalCost {
|
||||
pthread_mutex_lock(&_lock);
|
||||
NSUInteger totalCost = _lru->_totalCost;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
- (BOOL)releaseOnMainThread {
|
||||
pthread_mutex_lock(&_lock);
|
||||
BOOL releaseOnMainThread = _lru->_releaseOnMainThread;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return releaseOnMainThread;
|
||||
}
|
||||
|
||||
- (void)setReleaseOnMainThread:(BOOL)releaseOnMainThread {
|
||||
pthread_mutex_lock(&_lock);
|
||||
_lru->_releaseOnMainThread = releaseOnMainThread;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
- (BOOL)releaseAsynchronously {
|
||||
pthread_mutex_lock(&_lock);
|
||||
BOOL releaseAsynchronously = _lru->_releaseAsynchronously;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return releaseAsynchronously;
|
||||
}
|
||||
|
||||
- (void)setReleaseAsynchronously:(BOOL)releaseAsynchronously {
|
||||
pthread_mutex_lock(&_lock);
|
||||
_lru->_releaseAsynchronously = releaseAsynchronously;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
- (BOOL)containsObjectForKey:(id)key {
|
||||
if (!key) return NO;
|
||||
pthread_mutex_lock(&_lock);
|
||||
BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return contains;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)key {
|
||||
if (!key) return nil;
|
||||
pthread_mutex_lock(&_lock);
|
||||
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
|
||||
if (node) {
|
||||
node->_time = CACurrentMediaTime();
|
||||
[_lru bringNodeToHead:node];
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return node ? node->_value : nil;
|
||||
}
|
||||
|
||||
- (void)setObject:(id)object forKey:(id)key {
|
||||
[self setObject:object forKey:key withCost:0];
|
||||
}
|
||||
|
||||
- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
|
||||
if (!key) return;
|
||||
if (!object) {
|
||||
[self removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&_lock);
|
||||
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
if (node) {
|
||||
_lru->_totalCost -= node->_cost;
|
||||
_lru->_totalCost += cost;
|
||||
node->_cost = cost;
|
||||
node->_time = now;
|
||||
node->_value = object;
|
||||
[_lru bringNodeToHead:node];
|
||||
} else {
|
||||
node = [_YYLinkedMapNode new];
|
||||
node->_cost = cost;
|
||||
node->_time = now;
|
||||
node->_key = key;
|
||||
node->_value = object;
|
||||
[_lru insertNodeAtHead:node];
|
||||
}
|
||||
if (_lru->_totalCost > _costLimit) {
|
||||
dispatch_async(_queue, ^{
|
||||
[self trimToCost:_costLimit];
|
||||
});
|
||||
}
|
||||
if (_lru->_totalCount > _countLimit) {
|
||||
_YYLinkedMapNode *node = [_lru removeTailNode];
|
||||
if (_lru->_releaseAsynchronously) {
|
||||
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
[node class]; //hold and release in queue
|
||||
});
|
||||
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[node class]; //hold and release in queue
|
||||
});
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(id)key {
|
||||
if (!key) return;
|
||||
pthread_mutex_lock(&_lock);
|
||||
_YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
|
||||
if (node) {
|
||||
[_lru removeNode:node];
|
||||
if (_lru->_releaseAsynchronously) {
|
||||
dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue();
|
||||
dispatch_async(queue, ^{
|
||||
[node class]; //hold and release in queue
|
||||
});
|
||||
} else if (_lru->_releaseOnMainThread && !pthread_main_np()) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[node class]; //hold and release in queue
|
||||
});
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
pthread_mutex_lock(&_lock);
|
||||
[_lru removeAll];
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
- (void)trimToCount:(NSUInteger)count {
|
||||
if (count == 0) {
|
||||
[self removeAllObjects];
|
||||
return;
|
||||
}
|
||||
[self _trimToCount:count];
|
||||
}
|
||||
|
||||
- (void)trimToCost:(NSUInteger)cost {
|
||||
[self _trimToCost:cost];
|
||||
}
|
||||
|
||||
- (void)trimToAge:(NSTimeInterval)age {
|
||||
[self _trimToAge:age];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name];
|
||||
else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 ibireme <ibireme@gmail.com>
|
||||
|
||||
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,384 @@
|
|||
YYImage
|
||||
==============
|
||||
[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYImage/master/LICENSE)
|
||||
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
|
||||
[![CocoaPods](http://img.shields.io/cocoapods/v/YYImage.svg?style=flat)](http://cocoapods.org/?q= YYImage)
|
||||
[![CocoaPods](http://img.shields.io/cocoapods/p/YYImage.svg?style=flat)](http://cocoapods.org/?q= YYImage)
|
||||
[![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)
|
||||
[![Build Status](https://travis-ci.org/ibireme/YYImage.svg?branch=master)](https://travis-ci.org/ibireme/YYImage)
|
||||
|
||||
Image framework for iOS to display/encode/decode animated WebP, APNG, GIF, and more.<br/>
|
||||
(It's a component of [YYKit](https://github.com/ibireme/YYKit))
|
||||
|
||||
![niconiconi~](https://raw.github.com/ibireme/YYImage/master/Demo/YYImageDemo/niconiconi@2x.gif
|
||||
)
|
||||
|
||||
Features
|
||||
==============
|
||||
- Display/encode/decode animated image with these types:<br/> WebP, APNG, GIF.
|
||||
- Display/encode/decode still image with these types:<br/> WebP, PNG, GIF, JPEG, JP2, TIFF, BMP, ICO, ICNS.
|
||||
- Baseline/progressive/interlaced image decode with these types:<br/> PNG, GIF, JPEG, BMP.
|
||||
- Display frame based image animation and sprite sheet animation.
|
||||
- Dynamic memory buffer for lower memory usage.
|
||||
- Fully compatible with UIImage and UIImageView class.
|
||||
- Extendable protocol for custom image animation.
|
||||
- Fully documented.
|
||||
|
||||
Usage
|
||||
==============
|
||||
|
||||
###Display animated image
|
||||
|
||||
// File: ani@3x.gif
|
||||
UIImage *image = [YYImage imageNamed:@"ani.gif"];
|
||||
UIImageView *imageView = [[YYAnimatedImageView alloc] initWithImage:image];
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
|
||||
###Display frame animation
|
||||
|
||||
// Files: frame1.png, frame2.png, frame3.png
|
||||
NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"];
|
||||
NSArray *times = @[@0.1, @0.2, @0.1];
|
||||
UIImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES];
|
||||
UIImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
###Display sprite sheet animation
|
||||
|
||||
// 8 * 12 sprites in a single sheet image
|
||||
UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"];
|
||||
NSMutableArray *contentRects = [NSMutableArray new];
|
||||
NSMutableArray *durations = [NSMutableArray new];
|
||||
for (int j = 0; j < 12; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
CGRect rect;
|
||||
rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
rect.origin.x = img.size.width / 8 * i;
|
||||
rect.origin.y = img.size.height / 12 * j;
|
||||
[contentRects addObject:[NSValue valueWithCGRect:rect]];
|
||||
[durations addObject:@(1 / 60.0)];
|
||||
}
|
||||
}
|
||||
YYSpriteSheetImage *sprite;
|
||||
sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img
|
||||
contentRects:contentRects
|
||||
frameDurations:durations
|
||||
loopCount:0];
|
||||
YYAnimatedImageView *imageView = [YYAnimatedImageView new];
|
||||
imageView.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
imageView.image = sprite;
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
###Animation control
|
||||
|
||||
YYAnimatedImageView *imageView = ...;
|
||||
// pause:
|
||||
[imageView stopAnimating];
|
||||
// play:
|
||||
[imageView startAnimating];
|
||||
// set frame index:
|
||||
imageView.currentAnimatedImageIndex = 12;
|
||||
// get current status
|
||||
image.currentIsPlayingAnimation;
|
||||
|
||||
###Image decoder
|
||||
|
||||
// Decode single frame:
|
||||
NSData *data = [NSData dataWithContentsOfFile:@"/tmp/image.webp"];
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
|
||||
// Progressive:
|
||||
NSMutableData *data = [NSMutableData new];
|
||||
YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0];
|
||||
while(newDataArrived) {
|
||||
[data appendData:newData];
|
||||
[decoder updateData:data final:NO];
|
||||
if (decoder.frameCount > 0) {
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// progressive display...
|
||||
}
|
||||
}
|
||||
[decoder updateData:data final:YES];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// final display...
|
||||
|
||||
###Image encoder
|
||||
|
||||
// Encode still image:
|
||||
YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG];
|
||||
jpegEncoder.quality = 0.9;
|
||||
[jpegEncoder addImage:image duration:0];
|
||||
NSData jpegData = [jpegEncoder encode];
|
||||
|
||||
// Encode animated image:
|
||||
YYImageEncoder *webpEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP];
|
||||
webpEncoder.loopCount = 5;
|
||||
[webpEncoder addImage:image0 duration:0.1];
|
||||
[webpEncoder addImage:image1 duration:0.15];
|
||||
[webpEncoder addImage:image2 duration:0.2];
|
||||
NSData webpData = [webpEncoder encode];
|
||||
|
||||
###Image type detection
|
||||
|
||||
// Get image type from image data
|
||||
YYImageType type = YYImageDetectType(data);
|
||||
if (type == YYImageTypePNG) ...
|
||||
|
||||
|
||||
Installation
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. Update cocoapods to the latest version.
|
||||
2. Add `pod 'YYImage'` to your Podfile.
|
||||
3. Run `pod install` or `pod update`.
|
||||
4. Import \<YYImage/YYImage.h\>.
|
||||
5. Notice: it doesn't include WebP subspec by default, if you want to support WebP format, you may add `pod 'YYImage/WebP'` to your Podfile.
|
||||
|
||||
### Carthage
|
||||
|
||||
1. Add `github "ibireme/YYImage"` to your Cartfile.
|
||||
2. Run `carthage update --platform ios` and add the framework to your project.
|
||||
3. Import \<YYImage/YYImage.h\>.
|
||||
4. Notice: carthage framework doesn't include WebP component, if you want to support WebP format, use CocoaPods or install manually.
|
||||
|
||||
### Manually
|
||||
|
||||
1. Download all the files in the YYImage subdirectory.
|
||||
2. Add the source files to your Xcode project.
|
||||
3. Link with required frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* AssetsLibrary
|
||||
* ImageIO
|
||||
* Accelerate
|
||||
* MobileCoreServices
|
||||
* libz
|
||||
4. Import `YYImage.h`.
|
||||
5. Notice: if you want to support WebP format, you may add `Vendor/WebP.framework`(static library) to your Xcode project.
|
||||
|
||||
FAQ
|
||||
==============
|
||||
_Q: Why I can't display WebP image?_
|
||||
|
||||
A: Make sure you added the `WebP.framework` in your project. You may call `YYImageWebPAvailable()` to check whether the WebP subspec is installed correctly.
|
||||
|
||||
_Q: Why I can't play APNG animation?_
|
||||
|
||||
A: You should disable the `Compress PNG Files` and `Remove Text Metadata From PNG Files` in your project's build settings. Or you can rename your APNG file's extension name with `apng`.
|
||||
|
||||
Documentation
|
||||
==============
|
||||
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYImage/).<br/>
|
||||
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
==============
|
||||
This library requires `iOS 6.0+` and `Xcode 7.0+`.
|
||||
|
||||
|
||||
License
|
||||
==============
|
||||
YYImage is provided under the MIT license. See LICENSE file for details.
|
||||
|
||||
|
||||
<br/><br/>
|
||||
---
|
||||
中文介绍
|
||||
==============
|
||||
YYImage: 功能强大的 iOS 图像框架。<br/>
|
||||
(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一)
|
||||
|
||||
![niconiconi~](https://raw.github.com/ibireme/YYImage/master/Demo/YYImageDemo/niconiconi@2x.gif
|
||||
)
|
||||
|
||||
特性
|
||||
==============
|
||||
- 支持以下类型动画图像的播放/编码/解码:<br/>
|
||||
WebP, APNG, GIF。
|
||||
- 支持以下类型静态图像的显示/编码/解码:<br>
|
||||
WebP, PNG, GIF, JPEG, JP2, TIFF, BMP, ICO, ICNS。
|
||||
- 支持以下类型图片的渐进式/逐行扫描/隔行扫描解码:<br/>
|
||||
PNG, GIF, JPEG, BMP。
|
||||
- 支持多张图片构成的帧动画播放,支持单张图片的 sprite sheet 动画。
|
||||
- 高效的动态内存缓存管理,以保证高性能低内存的动画播放。
|
||||
- 完全兼容 UIImage 和 UIImageView,使用方便。
|
||||
- 保留可扩展的接口,以支持自定义动画。
|
||||
- 每个类和方法都有完善的文档注释。
|
||||
|
||||
|
||||
用法
|
||||
==============
|
||||
|
||||
###显示动画类型的图片
|
||||
|
||||
// 文件: ani@3x.gif
|
||||
UIImage *image = [YYImage imageNamed:@"ani.gif"];
|
||||
UIImageView *imageView = [[YYAnimatedImageView alloc] initWithImage:image];
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
|
||||
###播放帧动画
|
||||
|
||||
// 文件: frame1.png, frame2.png, frame3.png
|
||||
NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"];
|
||||
NSArray *times = @[@0.1, @0.2, @0.1];
|
||||
UIImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES];
|
||||
UIImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
###播放 sprite sheet 动画
|
||||
|
||||
// 8 * 12 sprites in a single sheet image
|
||||
UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"];
|
||||
NSMutableArray *contentRects = [NSMutableArray new];
|
||||
NSMutableArray *durations = [NSMutableArray new];
|
||||
for (int j = 0; j < 12; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
CGRect rect;
|
||||
rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
rect.origin.x = img.size.width / 8 * i;
|
||||
rect.origin.y = img.size.height / 12 * j;
|
||||
[contentRects addObject:[NSValue valueWithCGRect:rect]];
|
||||
[durations addObject:@(1 / 60.0)];
|
||||
}
|
||||
}
|
||||
YYSpriteSheetImage *sprite;
|
||||
sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img
|
||||
contentRects:contentRects
|
||||
frameDurations:durations
|
||||
loopCount:0];
|
||||
YYAnimatedImageView *imageView = [YYAnimatedImageView new];
|
||||
imageView.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
imageView.image = sprite;
|
||||
[self.view addSubView:imageView];
|
||||
|
||||
###动画播放控制
|
||||
|
||||
YYAnimatedImageView *imageView = ...;
|
||||
// 暂停:
|
||||
[imageView stopAnimating];
|
||||
// 播放:
|
||||
[imageView startAnimating];
|
||||
// 设置播放进度:
|
||||
imageView.currentAnimatedImageIndex = 12;
|
||||
// 获取播放状态:
|
||||
image.currentIsPlayingAnimation;
|
||||
//上面两个属性都支持 KVO。
|
||||
|
||||
###图片解码
|
||||
|
||||
// 解码单帧图片:
|
||||
NSData *data = [NSData dataWithContentsOfFile:@"/tmp/image.webp"];
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
|
||||
// 渐进式图片解码 (可用于图片下载显示):
|
||||
NSMutableData *data = [NSMutableData new];
|
||||
YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0];
|
||||
while(newDataArrived) {
|
||||
[data appendData:newData];
|
||||
[decoder updateData:data final:NO];
|
||||
if (decoder.frameCount > 0) {
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// progressive display...
|
||||
}
|
||||
}
|
||||
[decoder updateData:data final:YES];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// final display...
|
||||
|
||||
###图片编码
|
||||
|
||||
// 编码静态图 (支持各种常见图片格式):
|
||||
YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG];
|
||||
jpegEncoder.quality = 0.9;
|
||||
[jpegEncoder addImage:image duration:0];
|
||||
NSData jpegData = [jpegEncoder encode];
|
||||
|
||||
// 编码动态图 (支持 GIF/APNG/WebP):
|
||||
YYImageEncoder *webpEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP];
|
||||
webpEncoder.loopCount = 5;
|
||||
[webpEncoder addImage:image0 duration:0.1];
|
||||
[webpEncoder addImage:image1 duration:0.15];
|
||||
[webpEncoder addImage:image2 duration:0.2];
|
||||
NSData webpData = [webpEncoder encode];
|
||||
|
||||
###图片类型探测
|
||||
|
||||
// 获取图片类型
|
||||
YYImageType type = YYImageDetectType(data);
|
||||
if (type == YYImageTypePNG) ...
|
||||
|
||||
|
||||
安装
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. 将 cocoapods 更新至最新版本.
|
||||
2. 在 Podfile 中添加 `pod 'YYImage'`。
|
||||
3. 执行 `pod install` 或 `pod update`。
|
||||
4. 导入 \<YYImage/YYImage.h\>。
|
||||
5. 注意:pod 配置并没有包含 WebP 组件, 如果你需要支持 WebP,可以在 Podfile 中添加 `pod 'YYImage/WebP'`。
|
||||
|
||||
### Carthage
|
||||
|
||||
1. 在 Cartfile 中添加 `github "ibireme/YYImage"`。
|
||||
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
|
||||
3. 导入 \<YYImage/YYImage.h\>。
|
||||
4. 注意:carthage framework 并没有包含 WebP 组件。如果你需要支持 WebP,可以用 CocoaPods 安装,或者手动安装。
|
||||
|
||||
### 手动安装
|
||||
|
||||
1. 下载 YYImage 文件夹内的所有内容。
|
||||
2. 将 YYImage 内的源文件添加(拖放)到你的工程。
|
||||
3. 链接以下 frameworks:
|
||||
* UIKit
|
||||
* CoreFoundation
|
||||
* QuartzCore
|
||||
* AssetsLibrary
|
||||
* ImageIO
|
||||
* Accelerate
|
||||
* MobileCoreServices
|
||||
* libz
|
||||
4. 导入 `YYImage.h`。
|
||||
5. 注意:如果你需要支持 WebP,可以将 `Vendor/WebP.framework`(静态库) 加入你的工程。
|
||||
|
||||
常见问题
|
||||
==============
|
||||
_Q: 为什么我不能显示 WebP 图片?_
|
||||
|
||||
A: 确保 `WebP.framework` 已经被添加到你的工程内了。你可以调用 `YYImageWebPAvailable()` 来检查一下 WebP 组件是否被正确安装。
|
||||
|
||||
_Q: 为什么我不能播放 APNG 动画?_
|
||||
|
||||
A: 你应该禁用 Build Settings 中的 `Compress PNG Files` 和 `Remove Text Metadata From PNG Files`. 或者你也可以把 APNG 文件的扩展名改为`apng`.
|
||||
|
||||
文档
|
||||
==============
|
||||
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYImage/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
|
||||
|
||||
|
||||
系统要求
|
||||
==============
|
||||
该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。
|
||||
|
||||
|
||||
许可证
|
||||
==============
|
||||
YYImage 使用 MIT 许可证,详情见 LICENSE 文件。
|
||||
|
||||
|
||||
相关链接
|
||||
==============
|
||||
[移动端图片格式调研](http://blog.ibireme.com/2015/11/02/mobile_image_benchmark/)<br/>
|
||||
|
||||
[iOS 处理图片的一些小 Tip](http://blog.ibireme.com/2015/11/02/ios_image_tips/)
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/* src/webp/config.h. Generated from config.h.in by configure. */
|
||||
/* src/webp/config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if building universal (internal helper macro) */
|
||||
/* #undef AC_APPLE_UNIVERSAL_BUILD */
|
||||
|
||||
/* Set to 1 if __builtin_bswap16 is available */
|
||||
#define HAVE_BUILTIN_BSWAP16 1
|
||||
|
||||
/* Set to 1 if __builtin_bswap32 is available */
|
||||
#define HAVE_BUILTIN_BSWAP32 1
|
||||
|
||||
/* Set to 1 if __builtin_bswap64 is available */
|
||||
#define HAVE_BUILTIN_BSWAP64 1
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Define to 1 if you have the <GLUT/glut.h> header file. */
|
||||
/* #undef HAVE_GLUT_GLUT_H */
|
||||
|
||||
/* Define to 1 if you have the <GL/glut.h> header file. */
|
||||
/* #undef HAVE_GL_GLUT_H */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <OpenGL/glut.h> header file. */
|
||||
/* #undef HAVE_OPENGL_GLUT_H */
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
#define HAVE_PTHREAD_PRIO_INHERIT 1
|
||||
|
||||
/* Define to 1 if you have the <shlwapi.h> header file. */
|
||||
/* #undef HAVE_SHLWAPI_H */
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the <wincodec.h> header file. */
|
||||
/* #undef HAVE_WINCODEC_H */
|
||||
|
||||
/* Define to 1 if you have the <windows.h> header file. */
|
||||
/* #undef HAVE_WINDOWS_H */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "libwebp"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "https://bugs.chromium.org/p/webp"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "libwebp"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "libwebp 0.5.0"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "libwebp"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL "http://developers.google.com/speed/webp"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "0.5.0"
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
/* #undef PTHREAD_CREATE_JOINABLE */
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "0.5.0"
|
||||
|
||||
/* Enable experimental code */
|
||||
/* #undef WEBP_EXPERIMENTAL_FEATURES */
|
||||
|
||||
/* Define to 1 to force aligned memory operations */
|
||||
/* #undef WEBP_FORCE_ALIGNED */
|
||||
|
||||
/* Set to 1 if AVX2 is supported */
|
||||
/* #undef WEBP_HAVE_AVX2 */
|
||||
|
||||
/* Set to 1 if GIF library is installed */
|
||||
/* #undef WEBP_HAVE_GIF */
|
||||
|
||||
/* Set to 1 if OpenGL is supported */
|
||||
/* #undef WEBP_HAVE_GL */
|
||||
|
||||
/* Set to 1 if JPEG library is installed */
|
||||
/* #undef WEBP_HAVE_JPEG */
|
||||
|
||||
/* Set to 1 if PNG library is installed */
|
||||
/* #undef WEBP_HAVE_PNG */
|
||||
|
||||
/* Set to 1 if SSE2 is supported */
|
||||
/* #undef WEBP_HAVE_SSE2 */
|
||||
|
||||
/* Set to 1 if SSE4.1 is supported */
|
||||
/* #undef WEBP_HAVE_SSE41 */
|
||||
|
||||
/* Set to 1 if TIFF library is installed */
|
||||
/* #undef WEBP_HAVE_TIFF */
|
||||
|
||||
/* Undefine this to disable thread support. */
|
||||
#define WEBP_USE_THREAD 1
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
/* # undef WORDS_BIGENDIAN */
|
||||
# endif
|
||||
#endif
|
|
@ -0,0 +1,488 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Main decoding functions for WebP images.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_WEBP_DECODE_H_
|
||||
#define WEBP_WEBP_DECODE_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_DECODER_ABI_VERSION 0x0208 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum VP8StatusCode VP8StatusCode;
|
||||
// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;
|
||||
typedef struct WebPRGBABuffer WebPRGBABuffer;
|
||||
typedef struct WebPYUVABuffer WebPYUVABuffer;
|
||||
typedef struct WebPDecBuffer WebPDecBuffer;
|
||||
typedef struct WebPIDecoder WebPIDecoder;
|
||||
typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;
|
||||
typedef struct WebPDecoderOptions WebPDecoderOptions;
|
||||
typedef struct WebPDecoderConfig WebPDecoderConfig;
|
||||
|
||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetDecoderVersion(void);
|
||||
|
||||
// Retrieve basic header information: width, height.
|
||||
// This function will also validate the header and return 0 in
|
||||
// case of formatting error.
|
||||
// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
|
||||
WEBP_EXTERN(int) WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
|
||||
// with the dimensions in *width and *height. The ordering of samples in
|
||||
// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
|
||||
// The returned pointer should be deleted calling WebPFree().
|
||||
// Returns NULL in case of error.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeARGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
|
||||
// If the bitstream contains transparency, it is ignored.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
|
||||
// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
|
||||
// returned is the Y samples buffer. Upon return, *u and *v will point to
|
||||
// the U and V chroma data. These U and V buffers need NOT be passed to
|
||||
// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
|
||||
// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
|
||||
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
|
||||
// have a common stride returned as '*uv_stride'.
|
||||
// Return NULL in case of error.
|
||||
// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height,
|
||||
uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride);
|
||||
|
||||
// Releases memory returned by the WebPDecode*() functions above.
|
||||
WEBP_EXTERN(void) WebPFree(void* ptr);
|
||||
|
||||
// These five functions are variants of the above ones, that decode the image
|
||||
// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
|
||||
// available in this buffer is indicated by 'output_buffer_size'. If this
|
||||
// storage is not sufficient (or an error occurred), NULL is returned.
|
||||
// Otherwise, output_buffer is returned, for convenience.
|
||||
// The parameter 'output_stride' specifies the distance (in bytes)
|
||||
// between scanlines. Hence, output_buffer_size is expected to be at least
|
||||
// output_stride x picture-height.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeARGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// RGB and BGR variants. Here too the transparency information, if present,
|
||||
// will be dropped and ignored.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly
|
||||
// into pre-allocated luma/chroma plane buffers. This function requires the
|
||||
// strides to be passed: one for the luma plane and one for each of the
|
||||
// chroma ones. The size of each plane buffer is passed as 'luma_size',
|
||||
// 'u_size' and 'v_size' respectively.
|
||||
// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
|
||||
// during decoding (or because some buffers were found to be too small).
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeYUVInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Output colorspaces and buffer
|
||||
|
||||
// Colorspaces
|
||||
// Note: the naming describes the byte-ordering of packed samples in memory.
|
||||
// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
|
||||
// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
|
||||
// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:
|
||||
// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...
|
||||
// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
|
||||
// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for
|
||||
// these two modes:
|
||||
// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...
|
||||
// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...
|
||||
|
||||
typedef enum WEBP_CSP_MODE {
|
||||
MODE_RGB = 0, MODE_RGBA = 1,
|
||||
MODE_BGR = 2, MODE_BGRA = 3,
|
||||
MODE_ARGB = 4, MODE_RGBA_4444 = 5,
|
||||
MODE_RGB_565 = 6,
|
||||
// RGB-premultiplied transparent modes (alpha value is preserved)
|
||||
MODE_rgbA = 7,
|
||||
MODE_bgrA = 8,
|
||||
MODE_Argb = 9,
|
||||
MODE_rgbA_4444 = 10,
|
||||
// YUV modes must come after RGB ones.
|
||||
MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
|
||||
MODE_LAST = 13
|
||||
} WEBP_CSP_MODE;
|
||||
|
||||
// Some useful macros:
|
||||
static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
|
||||
return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||
|
||||
mode == MODE_rgbA_4444);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {
|
||||
return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
|
||||
mode == MODE_RGBA_4444 || mode == MODE_YUVA ||
|
||||
WebPIsPremultipliedMode(mode));
|
||||
}
|
||||
|
||||
static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
|
||||
return (mode < MODE_YUV);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer: Generic structure for describing the output sample buffer.
|
||||
|
||||
struct WebPRGBABuffer { // view as RGBA
|
||||
uint8_t* rgba; // pointer to RGBA samples
|
||||
int stride; // stride in bytes from one scanline to the next.
|
||||
size_t size; // total size of the *rgba buffer.
|
||||
};
|
||||
|
||||
struct WebPYUVABuffer { // view as YUVA
|
||||
uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples
|
||||
int y_stride; // luma stride
|
||||
int u_stride, v_stride; // chroma strides
|
||||
int a_stride; // alpha stride
|
||||
size_t y_size; // luma plane size
|
||||
size_t u_size, v_size; // chroma planes size
|
||||
size_t a_size; // alpha-plane size
|
||||
};
|
||||
|
||||
// Output buffer
|
||||
struct WebPDecBuffer {
|
||||
WEBP_CSP_MODE colorspace; // Colorspace.
|
||||
int width, height; // Dimensions.
|
||||
int is_external_memory; // If true, 'internal_memory' pointer is not used.
|
||||
union {
|
||||
WebPRGBABuffer RGBA;
|
||||
WebPYUVABuffer YUVA;
|
||||
} u; // Nameless union of buffer parameters.
|
||||
uint32_t pad[4]; // padding for later use
|
||||
|
||||
uint8_t* private_memory; // Internally allocated memory (only when
|
||||
// is_external_memory is false). Should not be used
|
||||
// externally, but accessed via the buffer union.
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecBufferInternal(WebPDecBuffer*, int);
|
||||
|
||||
// Initialize the structure as empty. Must be called before any other use.
|
||||
// Returns false in case of version mismatch
|
||||
static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
|
||||
return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Free any memory associated with the buffer. Must always be called last.
|
||||
// Note: doesn't free the 'buffer' structure itself.
|
||||
WEBP_EXTERN(void) WebPFreeDecBuffer(WebPDecBuffer* buffer);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Enumeration of the status codes
|
||||
|
||||
typedef enum VP8StatusCode {
|
||||
VP8_STATUS_OK = 0,
|
||||
VP8_STATUS_OUT_OF_MEMORY,
|
||||
VP8_STATUS_INVALID_PARAM,
|
||||
VP8_STATUS_BITSTREAM_ERROR,
|
||||
VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
VP8_STATUS_SUSPENDED,
|
||||
VP8_STATUS_USER_ABORT,
|
||||
VP8_STATUS_NOT_ENOUGH_DATA
|
||||
} VP8StatusCode;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Incremental decoding
|
||||
//
|
||||
// This API allows streamlined decoding of partial data.
|
||||
// Picture can be incrementally decoded as data become available thanks to the
|
||||
// WebPIDecoder object. This object can be left in a SUSPENDED state if the
|
||||
// picture is only partially decoded, pending additional input.
|
||||
// Code example:
|
||||
//
|
||||
// WebPInitDecBuffer(&buffer);
|
||||
// buffer.colorspace = mode;
|
||||
// ...
|
||||
// WebPIDecoder* idec = WebPINewDecoder(&buffer);
|
||||
// while (has_more_data) {
|
||||
// // ... (get additional data)
|
||||
// status = WebPIAppend(idec, new_data, new_data_size);
|
||||
// if (status != VP8_STATUS_SUSPENDED ||
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// // The above call decodes the current available buffer.
|
||||
// // Part of the image can now be refreshed by calling to
|
||||
// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||
// }
|
||||
// WebPIDelete(idec);
|
||||
|
||||
// Creates a new incremental decoder with the supplied buffer parameter.
|
||||
// This output_buffer can be passed NULL, in which case a default output buffer
|
||||
// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
|
||||
// is kept, which means that the lifespan of 'output_buffer' must be larger than
|
||||
// that of the returned WebPIDecoder object.
|
||||
// The supplied 'output_buffer' content MUST NOT be changed between calls to
|
||||
// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is
|
||||
// set to 1. In such a case, it is allowed to modify the pointers, size and
|
||||
// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
|
||||
// within valid bounds.
|
||||
// All other fields of WebPDecBuffer MUST remain constant between calls.
|
||||
// Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewDecoder(WebPDecBuffer* output_buffer);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the RGB/A samples specified by 'csp' into a preallocated
|
||||
// buffer 'output_buffer'. The size of this buffer is at least
|
||||
// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
|
||||
// is specified by 'output_stride'.
|
||||
// Additionally, output_buffer can be passed NULL in which case the output
|
||||
// buffer will be allocated automatically when the decoding starts. The
|
||||
// colorspace 'csp' is taken into account for allocating this buffer. All other
|
||||
// parameters are ignored.
|
||||
// Returns NULL if the allocation failed, or if some parameters are invalid.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewRGB(
|
||||
WEBP_CSP_MODE csp,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the raw luma/chroma samples into a preallocated planes if
|
||||
// supplied. The luma plane is specified by its pointer 'luma', its size
|
||||
// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane
|
||||
// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v
|
||||
// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer
|
||||
// can be pass NULL in case one is not interested in the transparency plane.
|
||||
// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.
|
||||
// In this case, the output buffer will be automatically allocated (using
|
||||
// MODE_YUVA) when decoding starts. All parameters are then ignored.
|
||||
// Returns NULL if the allocation failed or if a parameter is invalid.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUVA(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride);
|
||||
|
||||
// Deprecated version of the above, without the alpha plane.
|
||||
// Kept for backward compatibility.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUV(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride);
|
||||
|
||||
// Deletes the WebPIDecoder object and associated memory. Must always be called
|
||||
// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.
|
||||
WEBP_EXTERN(void) WebPIDelete(WebPIDecoder* idec);
|
||||
|
||||
// Copies and decodes the next available data. Returns VP8_STATUS_OK when
|
||||
// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
|
||||
// data is expected. Returns error in other cases.
|
||||
WEBP_EXTERN(VP8StatusCode) WebPIAppend(
|
||||
WebPIDecoder* idec, const uint8_t* data, size_t data_size);
|
||||
|
||||
// A variant of the above function to be used when data buffer contains
|
||||
// partial data from the beginning. In this case data buffer is not copied
|
||||
// to the internal memory.
|
||||
// Note that the value of the 'data' pointer can change between calls to
|
||||
// WebPIUpdate, for instance when the data buffer is resized to fit larger data.
|
||||
WEBP_EXTERN(VP8StatusCode) WebPIUpdate(
|
||||
WebPIDecoder* idec, const uint8_t* data, size_t data_size);
|
||||
|
||||
// Returns the RGB/A image decoded so far. Returns NULL if output params
|
||||
// are not initialized yet. The RGB/A output type corresponds to the colorspace
|
||||
// specified during call to WebPINewDecoder() or WebPINewRGB().
|
||||
// *last_y is the index of last decoded row in raster scan order. Some pointers
|
||||
// (*last_y, *width etc.) can be NULL if corresponding information is not
|
||||
// needed.
|
||||
WEBP_EXTERN(uint8_t*) WebPIDecGetRGB(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride);
|
||||
|
||||
// Same as above function to get a YUVA image. Returns pointer to the luma
|
||||
// plane or NULL in case of error. If there is no alpha information
|
||||
// the alpha pointer '*a' will be returned NULL.
|
||||
WEBP_EXTERN(uint8_t*) WebPIDecGetYUVA(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height, int* stride, int* uv_stride, int* a_stride);
|
||||
|
||||
// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
|
||||
// alpha information (if present). Kept for backward compatibility.
|
||||
static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
||||
const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
|
||||
int* width, int* height, int* stride, int* uv_stride) {
|
||||
return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
|
||||
stride, uv_stride, NULL);
|
||||
}
|
||||
|
||||
// Generic call to retrieve information about the displayable area.
|
||||
// If non NULL, the left/right/width/height pointers are filled with the visible
|
||||
// rectangular area so far.
|
||||
// Returns NULL in case the incremental decoder object is in an invalid state.
|
||||
// Otherwise returns the pointer to the internal representation. This structure
|
||||
// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
|
||||
WEBP_EXTERN(const WebPDecBuffer*) WebPIDecodedArea(
|
||||
const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Advanced decoding parametrization
|
||||
//
|
||||
// Code sample for using the advanced decoding API
|
||||
/*
|
||||
// A) Init a configuration object
|
||||
WebPDecoderConfig config;
|
||||
CHECK(WebPInitDecoderConfig(&config));
|
||||
|
||||
// B) optional: retrieve the bitstream's features.
|
||||
CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
|
||||
|
||||
// C) Adjust 'config', if needed
|
||||
config.no_fancy_upsampling = 1;
|
||||
config.output.colorspace = MODE_BGRA;
|
||||
// etc.
|
||||
|
||||
// Note that you can also make config.output point to an externally
|
||||
// supplied memory buffer, provided it's big enough to store the decoded
|
||||
// picture. Otherwise, config.output will just be used to allocate memory
|
||||
// and store the decoded picture.
|
||||
|
||||
// D) Decode!
|
||||
CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
|
||||
|
||||
// E) Decoded image is now in config.output (and config.output.u.RGBA)
|
||||
|
||||
// F) Reclaim memory allocated in config's object. It's safe to call
|
||||
// this function even if the memory is external and wasn't allocated
|
||||
// by WebPDecode().
|
||||
WebPFreeDecBuffer(&config.output);
|
||||
*/
|
||||
|
||||
// Features gathered from the bitstream
|
||||
struct WebPBitstreamFeatures {
|
||||
int width; // Width in pixels, as read from the bitstream.
|
||||
int height; // Height in pixels, as read from the bitstream.
|
||||
int has_alpha; // True if the bitstream contains an alpha channel.
|
||||
int has_animation; // True if the bitstream is an animation.
|
||||
int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless
|
||||
|
||||
uint32_t pad[5]; // padding for later use
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(VP8StatusCode) WebPGetFeaturesInternal(
|
||||
const uint8_t*, size_t, WebPBitstreamFeatures*, int);
|
||||
|
||||
// Retrieve features from the bitstream. The *features structure is filled
|
||||
// with information gathered from the bitstream.
|
||||
// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
|
||||
// features from headers. Returns error in other cases.
|
||||
static WEBP_INLINE VP8StatusCode WebPGetFeatures(
|
||||
const uint8_t* data, size_t data_size,
|
||||
WebPBitstreamFeatures* features) {
|
||||
return WebPGetFeaturesInternal(data, data_size, features,
|
||||
WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Decoding options
|
||||
struct WebPDecoderOptions {
|
||||
int bypass_filtering; // if true, skip the in-loop filtering
|
||||
int no_fancy_upsampling; // if true, use faster pointwise upsampler
|
||||
int use_cropping; // if true, cropping is applied _first_
|
||||
int crop_left, crop_top; // top-left position for cropping.
|
||||
// Will be snapped to even values.
|
||||
int crop_width, crop_height; // dimension of the cropping area
|
||||
int use_scaling; // if true, scaling is applied _afterward_
|
||||
int scaled_width, scaled_height; // final resolution
|
||||
int use_threads; // if true, use multi-threaded decoding
|
||||
int dithering_strength; // dithering strength (0=Off, 100=full)
|
||||
int flip; // flip output vertically
|
||||
int alpha_dithering_strength; // alpha dithering strength in [0..100]
|
||||
|
||||
uint32_t pad[5]; // padding for later use
|
||||
};
|
||||
|
||||
// Main object storing the configuration for advanced decoding.
|
||||
struct WebPDecoderConfig {
|
||||
WebPBitstreamFeatures input; // Immutable bitstream features (optional)
|
||||
WebPDecBuffer output; // Output buffer (can point to external mem)
|
||||
WebPDecoderOptions options; // Decoding options
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
|
||||
|
||||
// Initialize the configuration as empty. This function must always be
|
||||
// called first, unless WebPGetFeatures() is to be called.
|
||||
// Returns false in case of mismatched version.
|
||||
static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
|
||||
return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Instantiate a new incremental decoder object with the requested
|
||||
// configuration. The bitstream can be passed using 'data' and 'data_size'
|
||||
// parameter, in which case the features will be parsed and stored into
|
||||
// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
|
||||
// Note that 'config' can be NULL too, in which case a default configuration
|
||||
// is used.
|
||||
// The return WebPIDecoder object must always be deleted calling WebPIDelete().
|
||||
// Returns NULL in case of error (and config->status will then reflect
|
||||
// the error condition).
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
// Non-incremental version. This version decodes the full data at once, taking
|
||||
// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
|
||||
// if the decoding was successful).
|
||||
WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_DECODE_H_ */
|
|
@ -0,0 +1,358 @@
|
|||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Demux API.
|
||||
// Enables extraction of image and extended format data from WebP files.
|
||||
|
||||
// Code Example: Demuxing WebP data to extract all the frames, ICC profile
|
||||
// and EXIF/XMP metadata.
|
||||
/*
|
||||
WebPDemuxer* demux = WebPDemux(&webp_data);
|
||||
|
||||
uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
|
||||
uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
|
||||
// ... (Get information about the features present in the WebP file).
|
||||
uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
|
||||
|
||||
// ... (Iterate over all frames).
|
||||
WebPIterator iter;
|
||||
if (WebPDemuxGetFrame(demux, 1, &iter)) {
|
||||
do {
|
||||
// ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
|
||||
// ... and get other frame properties like width, height, offsets etc.
|
||||
// ... see 'struct WebPIterator' below for more info).
|
||||
} while (WebPDemuxNextFrame(&iter));
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
}
|
||||
|
||||
// ... (Extract metadata).
|
||||
WebPChunkIterator chunk_iter;
|
||||
if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
|
||||
// ... (Consume the ICC profile in 'chunk_iter.chunk').
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
|
||||
// ... (Consume the EXIF metadata in 'chunk_iter.chunk').
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
|
||||
// ... (Consume the XMP metadata in 'chunk_iter.chunk').
|
||||
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||
WebPDemuxDelete(demux);
|
||||
*/
|
||||
|
||||
#ifndef WEBP_WEBP_DEMUX_H_
|
||||
#define WEBP_WEBP_DEMUX_H_
|
||||
|
||||
#include "./decode.h" // for WEBP_CSP_MODE
|
||||
#include "./mux_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_DEMUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPDemuxState WebPDemuxState;
|
||||
// typedef enum WebPFormatFeature WebPFormatFeature;
|
||||
typedef struct WebPDemuxer WebPDemuxer;
|
||||
typedef struct WebPIterator WebPIterator;
|
||||
typedef struct WebPChunkIterator WebPChunkIterator;
|
||||
typedef struct WebPAnimInfo WebPAnimInfo;
|
||||
typedef struct WebPAnimDecoderOptions WebPAnimDecoderOptions;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns the version number of the demux library, packed in hexadecimal using
|
||||
// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetDemuxVersion(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a Demux object
|
||||
|
||||
typedef enum WebPDemuxState {
|
||||
WEBP_DEMUX_PARSE_ERROR = -1, // An error occurred while parsing.
|
||||
WEBP_DEMUX_PARSING_HEADER = 0, // Not enough data to parse full header.
|
||||
WEBP_DEMUX_PARSED_HEADER = 1, // Header parsing complete,
|
||||
// data may be available.
|
||||
WEBP_DEMUX_DONE = 2 // Entire file has been parsed.
|
||||
} WebPDemuxState;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(WebPDemuxer*) WebPDemuxInternal(
|
||||
const WebPData*, int, WebPDemuxState*, int);
|
||||
|
||||
// Parses the full WebP file given by 'data'. For single images the WebP file
|
||||
// header alone or the file header and the chunk header may be absent.
|
||||
// Returns a WebPDemuxer object on successful parse, NULL otherwise.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
|
||||
return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Parses the possibly incomplete WebP file given by 'data'.
|
||||
// If 'state' is non-NULL it will be set to indicate the status of the demuxer.
|
||||
// Returns NULL in case of error or if there isn't enough data to start parsing;
|
||||
// and a WebPDemuxer object on successful parse.
|
||||
// Note that WebPDemuxer keeps internal pointers to 'data' memory segment.
|
||||
// If this data is volatile, the demuxer object should be deleted (by calling
|
||||
// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data.
|
||||
// This is usually an inexpensive operation.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
|
||||
const WebPData* data, WebPDemuxState* state) {
|
||||
return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Frees memory associated with 'dmux'.
|
||||
WEBP_EXTERN(void) WebPDemuxDelete(WebPDemuxer* dmux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Data/information extraction.
|
||||
|
||||
typedef enum WebPFormatFeature {
|
||||
WEBP_FF_FORMAT_FLAGS, // Extended format flags present in the 'VP8X' chunk.
|
||||
WEBP_FF_CANVAS_WIDTH,
|
||||
WEBP_FF_CANVAS_HEIGHT,
|
||||
WEBP_FF_LOOP_COUNT,
|
||||
WEBP_FF_BACKGROUND_COLOR,
|
||||
WEBP_FF_FRAME_COUNT // Number of frames present in the demux object.
|
||||
// In case of a partial demux, this is the number of
|
||||
// frames seen so far, with the last frame possibly
|
||||
// being partial.
|
||||
} WebPFormatFeature;
|
||||
|
||||
// Get the 'feature' value from the 'dmux'.
|
||||
// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial()
|
||||
// returned a state > WEBP_DEMUX_PARSING_HEADER.
|
||||
WEBP_EXTERN(uint32_t) WebPDemuxGetI(
|
||||
const WebPDemuxer* dmux, WebPFormatFeature feature);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Frame iteration.
|
||||
|
||||
struct WebPIterator {
|
||||
int frame_num;
|
||||
int num_frames; // equivalent to WEBP_FF_FRAME_COUNT.
|
||||
int x_offset, y_offset; // offset relative to the canvas.
|
||||
int width, height; // dimensions of this frame.
|
||||
int duration; // display duration in milliseconds.
|
||||
WebPMuxAnimDispose dispose_method; // dispose method for the frame.
|
||||
int complete; // true if 'fragment' contains a full frame. partial images
|
||||
// may still be decoded with the WebP incremental decoder.
|
||||
WebPData fragment; // The frame given by 'frame_num'. Note for historical
|
||||
// reasons this is called a fragment.
|
||||
int has_alpha; // True if the frame contains transparency.
|
||||
WebPMuxAnimBlend blend_method; // Blend operation for the frame.
|
||||
|
||||
uint32_t pad[2]; // padding for later use.
|
||||
void* private_; // for internal use only.
|
||||
};
|
||||
|
||||
// Retrieves frame 'frame_number' from 'dmux'.
|
||||
// 'iter->fragment' points to the frame on return from this function.
|
||||
// Setting 'frame_number' equal to 0 will return the last frame of the image.
|
||||
// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
|
||||
// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of 'iter'.
|
||||
WEBP_EXTERN(int) WebPDemuxGetFrame(
|
||||
const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
|
||||
|
||||
// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
|
||||
// previous ('iter->frame_num' - 1) frame. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN(int) WebPDemuxNextFrame(WebPIterator* iter);
|
||||
WEBP_EXTERN(int) WebPDemuxPrevFrame(WebPIterator* iter);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
|
||||
// iter. Also, must be called before destroying the associated WebPDemuxer with
|
||||
// WebPDemuxDelete().
|
||||
WEBP_EXTERN(void) WebPDemuxReleaseIterator(WebPIterator* iter);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chunk iteration.
|
||||
|
||||
struct WebPChunkIterator {
|
||||
// The current and total number of chunks with the fourcc given to
|
||||
// WebPDemuxGetChunk().
|
||||
int chunk_num;
|
||||
int num_chunks;
|
||||
WebPData chunk; // The payload of the chunk.
|
||||
|
||||
uint32_t pad[6]; // padding for later use
|
||||
void* private_;
|
||||
};
|
||||
|
||||
// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from
|
||||
// 'dmux'.
|
||||
// 'fourcc' is a character array containing the fourcc of the chunk to return,
|
||||
// e.g., "ICCP", "XMP ", "EXIF", etc.
|
||||
// Setting 'chunk_number' equal to 0 will return the last chunk in a set.
|
||||
// Returns true if the chunk is found, false otherwise. Image related chunk
|
||||
// payloads are accessed through WebPDemuxGetFrame() and related functions.
|
||||
// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of the iterator.
|
||||
WEBP_EXTERN(int) WebPDemuxGetChunk(const WebPDemuxer* dmux,
|
||||
const char fourcc[4], int chunk_number,
|
||||
WebPChunkIterator* iter);
|
||||
|
||||
// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
|
||||
// ('iter->chunk_num' - 1) chunk. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN(int) WebPDemuxNextChunk(WebPChunkIterator* iter);
|
||||
WEBP_EXTERN(int) WebPDemuxPrevChunk(WebPChunkIterator* iter);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before destroying the associated WebPDemuxer with
|
||||
// WebPDemuxDelete().
|
||||
WEBP_EXTERN(void) WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPAnimDecoder API
|
||||
//
|
||||
// This API allows decoding (possibly) animated WebP images.
|
||||
//
|
||||
// Code Example:
|
||||
/*
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
WebPAnimDecoderOptionsInit(&dec_options);
|
||||
// Tune 'dec_options' as needed.
|
||||
WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
|
||||
WebPAnimInfo anim_info;
|
||||
WebPAnimDecoderGetInfo(dec, &anim_info);
|
||||
for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
|
||||
while (WebPAnimDecoderHasMoreFrames(dec)) {
|
||||
uint8_t* buf;
|
||||
int timestamp;
|
||||
WebPAnimDecoderGetNext(dec, &buf, ×tamp);
|
||||
// ... (Render 'buf' based on 'timestamp').
|
||||
// ... (Do NOT free 'buf', as it is owned by 'dec').
|
||||
}
|
||||
WebPAnimDecoderReset(dec);
|
||||
}
|
||||
const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
|
||||
// ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
|
||||
WebPAnimDecoderDelete(dec);
|
||||
*/
|
||||
|
||||
typedef struct WebPAnimDecoder WebPAnimDecoder; // Main opaque object.
|
||||
|
||||
// Global options.
|
||||
struct WebPAnimDecoderOptions {
|
||||
// Output colorspace. Only the following modes are supported:
|
||||
// MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA.
|
||||
WEBP_CSP_MODE color_mode;
|
||||
int use_threads; // If true, use multi-threaded decoding.
|
||||
uint32_t padding[7]; // Padding for later use.
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(int) WebPAnimDecoderOptionsInitInternal(
|
||||
WebPAnimDecoderOptions*, int);
|
||||
|
||||
// Should always be called, to initialize a fresh WebPAnimDecoderOptions
|
||||
// structure before modification. Returns false in case of version mismatch.
|
||||
// WebPAnimDecoderOptionsInit() must have succeeded before using the
|
||||
// 'dec_options' object.
|
||||
static WEBP_INLINE int WebPAnimDecoderOptionsInit(
|
||||
WebPAnimDecoderOptions* dec_options) {
|
||||
return WebPAnimDecoderOptionsInitInternal(dec_options,
|
||||
WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(WebPAnimDecoder*) WebPAnimDecoderNewInternal(
|
||||
const WebPData*, const WebPAnimDecoderOptions*, int);
|
||||
|
||||
// Creates and initializes a WebPAnimDecoder object.
|
||||
// Parameters:
|
||||
// webp_data - (in) WebP bitstream. This should remain unchanged during the
|
||||
// lifetime of the output WebPAnimDecoder object.
|
||||
// dec_options - (in) decoding options. Can be passed NULL to choose
|
||||
// reasonable defaults (in particular, color mode MODE_RGBA
|
||||
// will be picked).
|
||||
// Returns:
|
||||
// A pointer to the newly created WebPAnimDecoder object, or NULL in case of
|
||||
// parsing error, invalid option or memory error.
|
||||
static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
|
||||
const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
|
||||
return WebPAnimDecoderNewInternal(webp_data, dec_options,
|
||||
WEBP_DEMUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Global information about the animation..
|
||||
struct WebPAnimInfo {
|
||||
uint32_t canvas_width;
|
||||
uint32_t canvas_height;
|
||||
uint32_t loop_count;
|
||||
uint32_t bgcolor;
|
||||
uint32_t frame_count;
|
||||
uint32_t pad[4]; // padding for later use
|
||||
};
|
||||
|
||||
// Get global information about the animation.
|
||||
// Parameters:
|
||||
// dec - (in) decoder instance to get information from.
|
||||
// info - (out) global information fetched from the animation.
|
||||
// Returns:
|
||||
// True on success.
|
||||
WEBP_EXTERN(int) WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
|
||||
WebPAnimInfo* info);
|
||||
|
||||
// Fetch the next frame from 'dec' based on options supplied to
|
||||
// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
|
||||
// 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The
|
||||
// returned buffer 'buf' is valid only until the next call to
|
||||
// WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete().
|
||||
// Parameters:
|
||||
// dec - (in/out) decoder instance from which the next frame is to be fetched.
|
||||
// buf - (out) decoded frame.
|
||||
// timestamp - (out) timestamp of the frame in milliseconds.
|
||||
// Returns:
|
||||
// False if any of the arguments are NULL, or if there is a parsing or
|
||||
// decoding error, or if there are no more frames. Otherwise, returns true.
|
||||
WEBP_EXTERN(int) WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||
uint8_t** buf, int* timestamp);
|
||||
|
||||
// Check if there are more frames left to decode.
|
||||
// Parameters:
|
||||
// dec - (in) decoder instance to be checked.
|
||||
// Returns:
|
||||
// True if 'dec' is not NULL and some frames are yet to be decoded.
|
||||
// Otherwise, returns false.
|
||||
WEBP_EXTERN(int) WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
|
||||
|
||||
// Resets the WebPAnimDecoder object, so that next call to
|
||||
// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be
|
||||
// helpful when all frames need to be decoded multiple times (e.g.
|
||||
// info.loop_count times) without destroying and recreating the 'dec' object.
|
||||
// Parameters:
|
||||
// dec - (in/out) decoder instance to be reset
|
||||
WEBP_EXTERN(void) WebPAnimDecoderReset(WebPAnimDecoder* dec);
|
||||
|
||||
// Grab the internal demuxer object.
|
||||
// Getting the demuxer object can be useful if one wants to use operations only
|
||||
// available through demuxer; e.g. to get XMP/EXIF/ICC metadata. The returned
|
||||
// demuxer object is owned by 'dec' and is valid only until the next call to
|
||||
// WebPAnimDecoderDelete().
|
||||
//
|
||||
// Parameters:
|
||||
// dec - (in) decoder instance from which the demuxer object is to be fetched.
|
||||
WEBP_EXTERN(const WebPDemuxer*) WebPAnimDecoderGetDemuxer(
|
||||
const WebPAnimDecoder* dec);
|
||||
|
||||
// Deletes the WebPAnimDecoder object.
|
||||
// Parameters:
|
||||
// dec - (in/out) decoder instance to be deleted
|
||||
WEBP_EXTERN(void) WebPAnimDecoderDelete(WebPAnimDecoder* dec);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_DEMUX_H_ */
|
|
@ -0,0 +1,527 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP encoder: main interface
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_WEBP_ENCODE_H_
|
||||
#define WEBP_WEBP_ENCODE_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_ENCODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPImageHint WebPImageHint;
|
||||
// typedef enum WebPEncCSP WebPEncCSP;
|
||||
// typedef enum WebPPreset WebPPreset;
|
||||
// typedef enum WebPEncodingError WebPEncodingError;
|
||||
typedef struct WebPConfig WebPConfig;
|
||||
typedef struct WebPPicture WebPPicture; // main structure for I/O
|
||||
typedef struct WebPAuxStats WebPAuxStats;
|
||||
typedef struct WebPMemoryWriter WebPMemoryWriter;
|
||||
|
||||
// Return the encoder's version number, packed in hexadecimal using 8bits for
|
||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetEncoderVersion(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// One-stop-shop call! No questions asked:
|
||||
|
||||
// Returns the size of the compressed data (pointed to by *output), or 0 if
|
||||
// an error occurred. The compressed data must be released by the caller
|
||||
// using the call 'WebPFree(*output)'.
|
||||
// These functions compress using the lossy format, and the quality_factor
|
||||
// can go from 0 (smaller output, lower quality) to 100 (best quality,
|
||||
// larger output).
|
||||
WEBP_EXTERN(size_t) WebPEncodeRGB(const uint8_t* rgb,
|
||||
int width, int height, int stride,
|
||||
float quality_factor, uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeBGR(const uint8_t* bgr,
|
||||
int width, int height, int stride,
|
||||
float quality_factor, uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeRGBA(const uint8_t* rgba,
|
||||
int width, int height, int stride,
|
||||
float quality_factor, uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeBGRA(const uint8_t* bgra,
|
||||
int width, int height, int stride,
|
||||
float quality_factor, uint8_t** output);
|
||||
|
||||
// These functions are the equivalent of the above, but compressing in a
|
||||
// lossless manner. Files are usually larger than lossy format, but will
|
||||
// not suffer any compression loss.
|
||||
WEBP_EXTERN(size_t) WebPEncodeLosslessRGB(const uint8_t* rgb,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeLosslessBGR(const uint8_t* bgr,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeLosslessRGBA(const uint8_t* rgba,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
WEBP_EXTERN(size_t) WebPEncodeLosslessBGRA(const uint8_t* bgra,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
|
||||
// Releases memory returned by the WebPEncode*() functions above.
|
||||
WEBP_EXTERN(void) WebPFree(void* ptr);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Coding parameters
|
||||
|
||||
// Image characteristics hint for the underlying encoder.
|
||||
typedef enum WebPImageHint {
|
||||
WEBP_HINT_DEFAULT = 0, // default preset.
|
||||
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
|
||||
WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
|
||||
WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
|
||||
WEBP_HINT_LAST
|
||||
} WebPImageHint;
|
||||
|
||||
// Compression parameters.
|
||||
struct WebPConfig {
|
||||
int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
|
||||
float quality; // between 0 (smallest file) and 100 (biggest)
|
||||
int method; // quality/speed trade-off (0=fast, 6=slower-better)
|
||||
|
||||
WebPImageHint image_hint; // Hint for image type (lossless only for now).
|
||||
|
||||
// Parameters related to lossy compression only:
|
||||
int target_size; // if non-zero, set the desired target size in bytes.
|
||||
// Takes precedence over the 'compression' parameter.
|
||||
float target_PSNR; // if non-zero, specifies the minimal distortion to
|
||||
// try to achieve. Takes precedence over target_size.
|
||||
int segments; // maximum number of segments to use, in [1..4]
|
||||
int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum.
|
||||
int filter_strength; // range: [0 = off .. 100 = strongest]
|
||||
int filter_sharpness; // range: [0 = off .. 7 = least sharp]
|
||||
int filter_type; // filtering type: 0 = simple, 1 = strong (only used
|
||||
// if filter_strength > 0 or autofilter > 0)
|
||||
int autofilter; // Auto adjust filter's strength [0 = off, 1 = on]
|
||||
int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
|
||||
// 1 = compressed with WebP lossless). Default is 1.
|
||||
int alpha_filtering; // Predictive filtering method for alpha plane.
|
||||
// 0: none, 1: fast, 2: best. Default if 1.
|
||||
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
||||
// Default is 100.
|
||||
int pass; // number of entropy-analysis passes (in [1..10]).
|
||||
|
||||
int show_compressed; // if true, export the compressed picture back.
|
||||
// In-loop filtering is not applied.
|
||||
int preprocessing; // preprocessing filter:
|
||||
// 0=none, 1=segment-smooth, 2=pseudo-random dithering
|
||||
int partitions; // log2(number of token partitions) in [0..3]. Default
|
||||
// is set to 0 for easier progressive decoding.
|
||||
int partition_limit; // quality degradation allowed to fit the 512k limit
|
||||
// on prediction modes coding (0: no degradation,
|
||||
// 100: maximum possible degradation).
|
||||
int emulate_jpeg_size; // If true, compression parameters will be remapped
|
||||
// to better match the expected output size from
|
||||
// JPEG compression. Generally, the output size will
|
||||
// be similar but the degradation will be lower.
|
||||
int thread_level; // If non-zero, try and use multi-threaded encoding.
|
||||
int low_memory; // If set, reduce memory usage (but increase CPU use).
|
||||
|
||||
int near_lossless; // Near lossless encoding [0 = off(default) .. 100].
|
||||
// This feature is experimental.
|
||||
int exact; // if non-zero, preserve the exact RGB values under
|
||||
// transparent area. Otherwise, discard this invisible
|
||||
// RGB information for better compression. The default
|
||||
// value is 0.
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
int delta_palettization;
|
||||
uint32_t pad[2]; // padding for later use
|
||||
#else
|
||||
uint32_t pad[3]; // padding for later use
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
};
|
||||
|
||||
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||
// of source picture. These presets are used when calling WebPConfigPreset().
|
||||
typedef enum WebPPreset {
|
||||
WEBP_PRESET_DEFAULT = 0, // default preset.
|
||||
WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot
|
||||
WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting
|
||||
WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details
|
||||
WEBP_PRESET_ICON, // small-sized colorful images
|
||||
WEBP_PRESET_TEXT // text-like
|
||||
} WebPPreset;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
|
||||
|
||||
// Should always be called, to initialize a fresh WebPConfig structure before
|
||||
// modification. Returns false in case of version mismatch. WebPConfigInit()
|
||||
// must have succeeded before using the 'config' object.
|
||||
// Note that the default values are lossless=0 and quality=75.
|
||||
static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
|
||||
return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
|
||||
WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// This function will initialize the configuration according to a predefined
|
||||
// set of parameters (referred to by 'preset') and a given quality factor.
|
||||
// This function can be called as a replacement to WebPConfigInit(). Will
|
||||
// return false in case of error.
|
||||
static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
|
||||
WebPPreset preset, float quality) {
|
||||
return WebPConfigInitInternal(config, preset, quality,
|
||||
WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Activate the lossless compression mode with the desired efficiency level
|
||||
// between 0 (fastest, lowest compression) and 9 (slower, best compression).
|
||||
// A good default level is '6', providing a fair tradeoff between compression
|
||||
// speed and final compressed size.
|
||||
// This function will overwrite several fields from config: 'method', 'quality'
|
||||
// and 'lossless'. Returns false in case of parameter error.
|
||||
WEBP_EXTERN(int) WebPConfigLosslessPreset(WebPConfig* config, int level);
|
||||
|
||||
// Returns true if 'config' is non-NULL and all configuration parameters are
|
||||
// within their valid ranges.
|
||||
WEBP_EXTERN(int) WebPValidateConfig(const WebPConfig* config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Input / Output
|
||||
// Structure for storing auxiliary statistics (mostly for lossy encoding).
|
||||
|
||||
struct WebPAuxStats {
|
||||
int coded_size; // final size
|
||||
|
||||
float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
|
||||
int block_count[3]; // number of intra4/intra16/skipped macroblocks
|
||||
int header_bytes[2]; // approximate number of bytes spent for header
|
||||
// and mode-partition #0
|
||||
int residual_bytes[3][4]; // approximate number of bytes spent for
|
||||
// DC/AC/uv coefficients for each (0..3) segments.
|
||||
int segment_size[4]; // number of macroblocks in each segments
|
||||
int segment_quant[4]; // quantizer values for each segments
|
||||
int segment_level[4]; // filtering strength for each segments [0..63]
|
||||
|
||||
int alpha_data_size; // size of the transparency data
|
||||
int layer_data_size; // size of the enhancement layer data
|
||||
|
||||
// lossless encoder statistics
|
||||
uint32_t lossless_features; // bit0:predictor bit1:cross-color transform
|
||||
// bit2:subtract-green bit3:color indexing
|
||||
int histogram_bits; // number of precision bits of histogram
|
||||
int transform_bits; // precision bits for transform
|
||||
int cache_bits; // number of bits for color cache lookup
|
||||
int palette_size; // number of color in palette, if used
|
||||
int lossless_size; // final lossless size
|
||||
int lossless_hdr_size; // lossless header (transform, huffman etc) size
|
||||
int lossless_data_size; // lossless image data size
|
||||
|
||||
uint32_t pad[2]; // padding for later use
|
||||
};
|
||||
|
||||
// Signature for output function. Should return true if writing was successful.
|
||||
// data/data_size is the segment of data to write, and 'picture' is for
|
||||
// reference (and so one can make use of picture->custom_ptr).
|
||||
typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
|
||||
const WebPPicture* picture);
|
||||
|
||||
// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using
|
||||
// the following WebPMemoryWriter object (to be set as a custom_ptr).
|
||||
struct WebPMemoryWriter {
|
||||
uint8_t* mem; // final buffer (of size 'max_size', larger than 'size').
|
||||
size_t size; // final size
|
||||
size_t max_size; // total capacity
|
||||
uint32_t pad[1]; // padding for later use
|
||||
};
|
||||
|
||||
// The following must be called first before any use.
|
||||
WEBP_EXTERN(void) WebPMemoryWriterInit(WebPMemoryWriter* writer);
|
||||
|
||||
// The following must be called to deallocate writer->mem memory. The 'writer'
|
||||
// object itself is not deallocated.
|
||||
WEBP_EXTERN(void) WebPMemoryWriterClear(WebPMemoryWriter* writer);
|
||||
// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
|
||||
// completion, writer.mem and writer.size will hold the coded data.
|
||||
// writer.mem must be freed by calling WebPMemoryWriterClear.
|
||||
WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
||||
const WebPPicture* picture);
|
||||
|
||||
// Progress hook, called from time to time to report progress. It can return
|
||||
// false to request an abort of the encoding process, or true otherwise if
|
||||
// everything is OK.
|
||||
typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture);
|
||||
|
||||
// Color spaces.
|
||||
typedef enum WebPEncCSP {
|
||||
// chroma sampling
|
||||
WEBP_YUV420 = 0, // 4:2:0
|
||||
WEBP_YUV420A = 4, // alpha channel variant
|
||||
WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors
|
||||
WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present
|
||||
} WebPEncCSP;
|
||||
|
||||
// Encoding error conditions.
|
||||
typedef enum WebPEncodingError {
|
||||
VP8_ENC_OK = 0,
|
||||
VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects
|
||||
VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits
|
||||
VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL
|
||||
VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid
|
||||
VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height
|
||||
VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k
|
||||
VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M
|
||||
VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes
|
||||
VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G
|
||||
VP8_ENC_ERROR_USER_ABORT, // abort request by user
|
||||
VP8_ENC_ERROR_LAST // list terminator. always last.
|
||||
} WebPEncodingError;
|
||||
|
||||
// maximum width/height allowed (inclusive), in pixels
|
||||
#define WEBP_MAX_DIMENSION 16383
|
||||
|
||||
// Main exchange structure (input samples, output bytes, statistics)
|
||||
struct WebPPicture {
|
||||
// INPUT
|
||||
//////////////
|
||||
// Main flag for encoder selecting between ARGB or YUV input.
|
||||
// It is recommended to use ARGB input (*argb, argb_stride) for lossless
|
||||
// compression, and YUV input (*y, *u, *v, etc.) for lossy compression
|
||||
// since these are the respective native colorspace for these formats.
|
||||
int use_argb;
|
||||
|
||||
// YUV input (mostly used for input to lossy compression)
|
||||
WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr).
|
||||
int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION)
|
||||
uint8_t *y, *u, *v; // pointers to luma/chroma planes.
|
||||
int y_stride, uv_stride; // luma/chroma strides.
|
||||
uint8_t* a; // pointer to the alpha plane
|
||||
int a_stride; // stride of the alpha plane
|
||||
uint32_t pad1[2]; // padding for later use
|
||||
|
||||
// ARGB input (mostly used for input to lossless compression)
|
||||
uint32_t* argb; // Pointer to argb (32 bit) plane.
|
||||
int argb_stride; // This is stride in pixels units, not bytes.
|
||||
uint32_t pad2[3]; // padding for later use
|
||||
|
||||
// OUTPUT
|
||||
///////////////
|
||||
// Byte-emission hook, to store compressed bytes as they are ready.
|
||||
WebPWriterFunction writer; // can be NULL
|
||||
void* custom_ptr; // can be used by the writer.
|
||||
|
||||
// map for extra information (only for lossy compression mode)
|
||||
int extra_info_type; // 1: intra type, 2: segment, 3: quant
|
||||
// 4: intra-16 prediction mode,
|
||||
// 5: chroma prediction mode,
|
||||
// 6: bit cost, 7: distortion
|
||||
uint8_t* extra_info; // if not NULL, points to an array of size
|
||||
// ((width + 15) / 16) * ((height + 15) / 16) that
|
||||
// will be filled with a macroblock map, depending
|
||||
// on extra_info_type.
|
||||
|
||||
// STATS AND REPORTS
|
||||
///////////////////////////
|
||||
// Pointer to side statistics (updated only if not NULL)
|
||||
WebPAuxStats* stats;
|
||||
|
||||
// Error code for the latest error encountered during encoding
|
||||
WebPEncodingError error_code;
|
||||
|
||||
// If not NULL, report progress during encoding.
|
||||
WebPProgressHook progress_hook;
|
||||
|
||||
void* user_data; // this field is free to be set to any value and
|
||||
// used during callbacks (like progress-report e.g.).
|
||||
|
||||
uint32_t pad3[3]; // padding for later use
|
||||
|
||||
// Unused for now
|
||||
uint8_t *pad4, *pad5;
|
||||
uint32_t pad6[8]; // padding for later use
|
||||
|
||||
// PRIVATE FIELDS
|
||||
////////////////////
|
||||
void* memory_; // row chunk of memory for yuva planes
|
||||
void* memory_argb_; // and for argb too.
|
||||
void* pad7[2]; // padding for later use
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPPictureInitInternal(WebPPicture*, int);
|
||||
|
||||
// Should always be called, to initialize the structure. Returns false in case
|
||||
// of version mismatch. WebPPictureInit() must have succeeded before using the
|
||||
// 'picture' object.
|
||||
// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
|
||||
static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
|
||||
return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPPicture utils
|
||||
|
||||
// Convenience allocation / deallocation based on picture->width/height:
|
||||
// Allocate y/u/v buffers as per colorspace/width/height specification.
|
||||
// Note! This function will free the previous buffer if needed.
|
||||
// Returns false in case of memory error.
|
||||
WEBP_EXTERN(int) WebPPictureAlloc(WebPPicture* picture);
|
||||
|
||||
// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
|
||||
// Note that this function does _not_ free the memory used by the 'picture'
|
||||
// object itself.
|
||||
// Besides memory (which is reclaimed) all other fields of 'picture' are
|
||||
// preserved.
|
||||
WEBP_EXTERN(void) WebPPictureFree(WebPPicture* picture);
|
||||
|
||||
// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst
|
||||
// will fully own the copied pixels (this is not a view). The 'dst' picture need
|
||||
// not be initialized as its content is overwritten.
|
||||
// Returns false in case of memory allocation error.
|
||||
WEBP_EXTERN(int) WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
|
||||
|
||||
// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
|
||||
// are in dB, stored in result[] in the Y/U/V/Alpha/All or B/G/R/A/All order.
|
||||
// Returns false in case of error (src and ref don't have same dimension, ...)
|
||||
// Warning: this function is rather CPU-intensive.
|
||||
WEBP_EXTERN(int) WebPPictureDistortion(
|
||||
const WebPPicture* src, const WebPPicture* ref,
|
||||
int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
|
||||
float result[5]);
|
||||
|
||||
// self-crops a picture to the rectangle defined by top/left/width/height.
|
||||
// Returns false in case of memory allocation error, or if the rectangle is
|
||||
// outside of the source picture.
|
||||
// The rectangle for the view is defined by the top-left corner pixel
|
||||
// coordinates (left, top) as well as its width and height. This rectangle
|
||||
// must be fully be comprised inside the 'src' source picture. If the source
|
||||
// picture uses the YUV420 colorspace, the top and left coordinates will be
|
||||
// snapped to even values.
|
||||
WEBP_EXTERN(int) WebPPictureCrop(WebPPicture* picture,
|
||||
int left, int top, int width, int height);
|
||||
|
||||
// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
|
||||
// is defined by the top-left corner pixel coordinates (left, top) as well
|
||||
// as its width and height. This rectangle must be fully be comprised inside
|
||||
// the 'src' source picture. If the source picture uses the YUV420 colorspace,
|
||||
// the top and left coordinates will be snapped to even values.
|
||||
// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed
|
||||
// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so,
|
||||
// the original dimension will be lost). Picture 'dst' need not be initialized
|
||||
// with WebPPictureInit() if it is different from 'src', since its content will
|
||||
// be overwritten.
|
||||
// Returns false in case of memory allocation error or invalid parameters.
|
||||
WEBP_EXTERN(int) WebPPictureView(const WebPPicture* src,
|
||||
int left, int top, int width, int height,
|
||||
WebPPicture* dst);
|
||||
|
||||
// Returns true if the 'picture' is actually a view and therefore does
|
||||
// not own the memory for pixels.
|
||||
WEBP_EXTERN(int) WebPPictureIsView(const WebPPicture* picture);
|
||||
|
||||
// Rescale a picture to new dimension width x height.
|
||||
// If either 'width' or 'height' (but not both) is 0 the corresponding
|
||||
// dimension will be calculated preserving the aspect ratio.
|
||||
// No gamma correction is applied.
|
||||
// Returns false in case of error (invalid parameter or insufficient memory).
|
||||
WEBP_EXTERN(int) WebPPictureRescale(WebPPicture* pic, int width, int height);
|
||||
|
||||
// Colorspace conversion function to import RGB samples.
|
||||
// Previous buffer will be free'd, if any.
|
||||
// *rgb buffer should have a size of at least height * rgb_stride.
|
||||
// Returns false in case of memory error.
|
||||
WEBP_EXTERN(int) WebPPictureImportRGB(
|
||||
WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
|
||||
// Same, but for RGBA buffer.
|
||||
WEBP_EXTERN(int) WebPPictureImportRGBA(
|
||||
WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
|
||||
// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
|
||||
// input buffer ignoring the alpha channel. Avoids needing to copy the data
|
||||
// to a temporary 24-bit RGB buffer to import the RGB only.
|
||||
WEBP_EXTERN(int) WebPPictureImportRGBX(
|
||||
WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
|
||||
|
||||
// Variants of the above, but taking BGR(A|X) input.
|
||||
WEBP_EXTERN(int) WebPPictureImportBGR(
|
||||
WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
|
||||
WEBP_EXTERN(int) WebPPictureImportBGRA(
|
||||
WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
|
||||
WEBP_EXTERN(int) WebPPictureImportBGRX(
|
||||
WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
|
||||
|
||||
// Converts picture->argb data to the YUV420A format. The 'colorspace'
|
||||
// parameter is deprecated and should be equal to WEBP_YUV420.
|
||||
// Upon return, picture->use_argb is set to false. The presence of real
|
||||
// non-opaque transparent values is detected, and 'colorspace' will be
|
||||
// adjusted accordingly. Note that this method is lossy.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureARGBToYUVA(WebPPicture* picture,
|
||||
WebPEncCSP /*colorspace = WEBP_YUV420*/);
|
||||
|
||||
// Same as WebPPictureARGBToYUVA(), but the conversion is done using
|
||||
// pseudo-random dithering with a strength 'dithering' between
|
||||
// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
|
||||
// for photographic picture.
|
||||
WEBP_EXTERN(int) WebPPictureARGBToYUVADithered(
|
||||
WebPPicture* picture, WebPEncCSP colorspace, float dithering);
|
||||
|
||||
// Performs 'smart' RGBA->YUVA420 downsampling and colorspace conversion.
|
||||
// Downsampling is handled with extra care in case of color clipping. This
|
||||
// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
|
||||
// YUV representation.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
||||
|
||||
// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
|
||||
// The input format must be YUV_420 or YUV_420A.
|
||||
// Note that the use of this method is discouraged if one has access to the
|
||||
// raw ARGB samples, since using YUV420 is comparatively lossy. Also, the
|
||||
// conversion from YUV420 to ARGB incurs a small loss too.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureYUVAToARGB(WebPPicture* picture);
|
||||
|
||||
// Helper function: given a width x height plane of RGBA or YUV(A) samples
|
||||
// clean-up the YUV or RGB samples under fully transparent area, to help
|
||||
// compressibility (no guarantee, though).
|
||||
WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* picture);
|
||||
|
||||
// Scan the picture 'picture' for the presence of non fully opaque alpha values.
|
||||
// Returns true in such case. Otherwise returns false (indicating that the
|
||||
// alpha plane can be ignored altogether e.g.).
|
||||
WEBP_EXTERN(int) WebPPictureHasTransparency(const WebPPicture* picture);
|
||||
|
||||
// Remove the transparency information (if present) by blending the color with
|
||||
// the background color 'background_rgb' (specified as 24bit RGB triplet).
|
||||
// After this call, all alpha values are reset to 0xff.
|
||||
WEBP_EXTERN(void) WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main call
|
||||
|
||||
// Main encoding call, after config and picture have been initialized.
|
||||
// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION),
|
||||
// and the 'config' object must be a valid one.
|
||||
// Returns false in case of error, true otherwise.
|
||||
// In case of error, picture->error_code is updated accordingly.
|
||||
// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending
|
||||
// on the value of 'picture->use_argb'. It is highly recommended to use
|
||||
// the former for lossy encoding, and the latter for lossless encoding
|
||||
// (when config.lossless is true). Automatic conversion from one format to
|
||||
// another is provided but they both incur some loss.
|
||||
WEBP_EXTERN(int) WebPEncode(const WebPConfig* config, WebPPicture* picture);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_ENCODE_H_ */
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef WEBP_WEBP_EXTRAS_H_
|
||||
#define WEBP_WEBP_EXTRAS_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "./encode.h"
|
||||
|
||||
#define WEBP_EXTRAS_ABI_VERSION 0x0000 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns the version number of the extras library, packed in hexadecimal using
|
||||
// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetExtrasVersion(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Ad-hoc colorspace importers.
|
||||
|
||||
// Import luma sample (gray scale image) into 'picture'. The 'picture'
|
||||
// width and height must be set prior to calling this function.
|
||||
WEBP_EXTERN(int) WebPImportGray(const uint8_t* gray, WebPPicture* picture);
|
||||
|
||||
// Import rgb sample in RGB565 packed format into 'picture'. The 'picture'
|
||||
// width and height must be set prior to calling this function.
|
||||
WEBP_EXTERN(int) WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic);
|
||||
|
||||
// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture'
|
||||
// width and height must be set prior to calling this function.
|
||||
WEBP_EXTERN(int) WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_EXTRAS_H_ */
|
88
ProductApp/Pods/YYImage/Vendor/WebP.framework/Headers/format_constants.h
generated
vendored
Normal file
88
ProductApp/Pods/YYImage/Vendor/WebP.framework/Headers/format_constants.h
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal header for constants related to WebP file format.
|
||||
//
|
||||
// Author: Urvang (urvang@google.com)
|
||||
|
||||
#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_
|
||||
#define WEBP_WEBP_FORMAT_CONSTANTS_H_
|
||||
|
||||
// Create fourcc of the chunk from the chunk tag characters.
|
||||
#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24)
|
||||
|
||||
// VP8 related constants.
|
||||
#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data.
|
||||
#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
|
||||
#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition
|
||||
#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data.
|
||||
|
||||
// VP8L related constants.
|
||||
#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size.
|
||||
#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte.
|
||||
#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store
|
||||
// width and height.
|
||||
#define VP8L_VERSION_BITS 3 // 3 bits reserved for version.
|
||||
#define VP8L_VERSION 0 // version 0
|
||||
#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header.
|
||||
|
||||
#define MAX_PALETTE_SIZE 256
|
||||
#define MAX_CACHE_BITS 11
|
||||
#define HUFFMAN_CODES_PER_META_CODE 5
|
||||
#define ARGB_BLACK 0xff000000
|
||||
|
||||
#define DEFAULT_CODE_LENGTH 8
|
||||
#define MAX_ALLOWED_CODE_LENGTH 15
|
||||
|
||||
#define NUM_LITERAL_CODES 256
|
||||
#define NUM_LENGTH_CODES 24
|
||||
#define NUM_DISTANCE_CODES 40
|
||||
#define CODE_LENGTH_CODES 19
|
||||
|
||||
#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits
|
||||
#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits
|
||||
|
||||
#define TRANSFORM_PRESENT 1 // The bit to be written when next data
|
||||
// to be read is a transform.
|
||||
#define NUM_TRANSFORMS 4 // Maximum number of allowed transform
|
||||
// in a bitstream.
|
||||
typedef enum {
|
||||
PREDICTOR_TRANSFORM = 0,
|
||||
CROSS_COLOR_TRANSFORM = 1,
|
||||
SUBTRACT_GREEN = 2,
|
||||
COLOR_INDEXING_TRANSFORM = 3
|
||||
} VP8LImageTransformType;
|
||||
|
||||
// Alpha related constants.
|
||||
#define ALPHA_HEADER_LEN 1
|
||||
#define ALPHA_NO_COMPRESSION 0
|
||||
#define ALPHA_LOSSLESS_COMPRESSION 1
|
||||
#define ALPHA_PREPROCESSED_LEVELS 1
|
||||
|
||||
// Mux related constants.
|
||||
#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L").
|
||||
#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
|
||||
#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
|
||||
#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
|
||||
#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk.
|
||||
#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk.
|
||||
#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk.
|
||||
#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
|
||||
|
||||
#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height.
|
||||
#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
|
||||
#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
|
||||
#define MAX_DURATION (1 << 24) // maximum duration
|
||||
#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset
|
||||
|
||||
// Maximum chunk payload is such that adding the header and padding won't
|
||||
// overflow a uint32_t.
|
||||
#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1)
|
||||
|
||||
#endif /* WEBP_WEBP_FORMAT_CONSTANTS_H_ */
|
|
@ -0,0 +1,530 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// RIFF container manipulation and encoding for WebP images.
|
||||
//
|
||||
// Authors: Urvang (urvang@google.com)
|
||||
// Vikas (vikasa@google.com)
|
||||
|
||||
#ifndef WEBP_WEBP_MUX_H_
|
||||
#define WEBP_WEBP_MUX_H_
|
||||
|
||||
#include "./mux_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_MUX_ABI_VERSION 0x0106 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Mux API
|
||||
//
|
||||
// This API allows manipulation of WebP container images containing features
|
||||
// like color profile, metadata, animation and fragmented images.
|
||||
//
|
||||
// Code Example#1: Create a WebPMux object with image data, color profile and
|
||||
// XMP metadata.
|
||||
/*
|
||||
int copy_data = 0;
|
||||
WebPMux* mux = WebPMuxNew();
|
||||
// ... (Prepare image data).
|
||||
WebPMuxSetImage(mux, &image, copy_data);
|
||||
// ... (Prepare ICCP color profile data).
|
||||
WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
|
||||
// ... (Prepare XMP metadata).
|
||||
WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
|
||||
// Get data from mux in WebP RIFF format.
|
||||
WebPMuxAssemble(mux, &output_data);
|
||||
WebPMuxDelete(mux);
|
||||
// ... (Consume output_data; e.g. write output_data.bytes to file).
|
||||
WebPDataClear(&output_data);
|
||||
*/
|
||||
|
||||
// Code Example#2: Get image and color profile data from a WebP file.
|
||||
/*
|
||||
int copy_data = 0;
|
||||
// ... (Read data from file).
|
||||
WebPMux* mux = WebPMuxCreate(&data, copy_data);
|
||||
WebPMuxGetFrame(mux, 1, &image);
|
||||
// ... (Consume image; e.g. call WebPDecode() to decode the data).
|
||||
WebPMuxGetChunk(mux, "ICCP", &icc_profile);
|
||||
// ... (Consume icc_data).
|
||||
WebPMuxDelete(mux);
|
||||
free(data);
|
||||
*/
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPMuxError WebPMuxError;
|
||||
// typedef enum WebPChunkId WebPChunkId;
|
||||
typedef struct WebPMux WebPMux; // main opaque object.
|
||||
typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
|
||||
typedef struct WebPMuxAnimParams WebPMuxAnimParams;
|
||||
typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
|
||||
|
||||
// Error codes
|
||||
typedef enum WebPMuxError {
|
||||
WEBP_MUX_OK = 1,
|
||||
WEBP_MUX_NOT_FOUND = 0,
|
||||
WEBP_MUX_INVALID_ARGUMENT = -1,
|
||||
WEBP_MUX_BAD_DATA = -2,
|
||||
WEBP_MUX_MEMORY_ERROR = -3,
|
||||
WEBP_MUX_NOT_ENOUGH_DATA = -4
|
||||
} WebPMuxError;
|
||||
|
||||
// IDs for different types of chunks.
|
||||
typedef enum WebPChunkId {
|
||||
WEBP_CHUNK_VP8X, // VP8X
|
||||
WEBP_CHUNK_ICCP, // ICCP
|
||||
WEBP_CHUNK_ANIM, // ANIM
|
||||
WEBP_CHUNK_ANMF, // ANMF
|
||||
WEBP_CHUNK_FRGM, // FRGM
|
||||
WEBP_CHUNK_ALPHA, // ALPH
|
||||
WEBP_CHUNK_IMAGE, // VP8/VP8L
|
||||
WEBP_CHUNK_EXIF, // EXIF
|
||||
WEBP_CHUNK_XMP, // XMP
|
||||
WEBP_CHUNK_UNKNOWN, // Other chunks.
|
||||
WEBP_CHUNK_NIL
|
||||
} WebPChunkId;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns the version number of the mux library, packed in hexadecimal using
|
||||
// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetMuxVersion(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a Mux object
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(WebPMux*) WebPNewInternal(int);
|
||||
|
||||
// Creates an empty mux object.
|
||||
// Returns:
|
||||
// A pointer to the newly created empty mux object.
|
||||
// Or NULL in case of memory error.
|
||||
static WEBP_INLINE WebPMux* WebPMuxNew(void) {
|
||||
return WebPNewInternal(WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Deletes the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to be deleted
|
||||
WEBP_EXTERN(void) WebPMuxDelete(WebPMux* mux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Mux creation.
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(WebPMux*) WebPMuxCreateInternal(const WebPData*, int, int);
|
||||
|
||||
// Creates a mux object from raw data given in WebP RIFF format.
|
||||
// Parameters:
|
||||
// bitstream - (in) the bitstream data in WebP RIFF format
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// A pointer to the mux object created from given data - on success.
|
||||
// NULL - In case of invalid data or memory error.
|
||||
static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
|
||||
int copy_data) {
|
||||
return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Non-image chunks.
|
||||
|
||||
// Note: Only non-image related chunks should be managed through chunk APIs.
|
||||
// (Image related chunks are: "ANMF", "FRGM", "VP8 ", "VP8L" and "ALPH").
|
||||
// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(),
|
||||
// WebPMuxGetFrame() and WebPMuxDeleteFrame().
|
||||
|
||||
// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object.
|
||||
// Any existing chunk(s) with the same id will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which the chunk is to be added
|
||||
// fourcc - (in) a character array containing the fourcc of the given chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// chunk_data - (in) the chunk data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetChunk(
|
||||
WebPMux* mux, const char fourcc[4], const WebPData* chunk_data,
|
||||
int copy_data);
|
||||
|
||||
// Gets a reference to the data of the chunk with id 'fourcc' in the mux object.
|
||||
// The caller should NOT free the returned data.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the chunk data is to be fetched
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// chunk_data - (out) returned chunk data
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetChunk(
|
||||
const WebPMux* mux, const char fourcc[4], WebPData* chunk_data);
|
||||
|
||||
// Deletes the chunk with the given 'fourcc' from the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which the chunk is to be deleted
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk(
|
||||
WebPMux* mux, const char fourcc[4]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Images.
|
||||
|
||||
// Encapsulates data about a single frame/fragment.
|
||||
struct WebPMuxFrameInfo {
|
||||
WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream
|
||||
// or a single-image WebP file.
|
||||
int x_offset; // x-offset of the frame.
|
||||
int y_offset; // y-offset of the frame.
|
||||
int duration; // duration of the frame (in milliseconds).
|
||||
|
||||
WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF,
|
||||
// WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE
|
||||
WebPMuxAnimDispose dispose_method; // Disposal method for the frame.
|
||||
WebPMuxAnimBlend blend_method; // Blend operation for the frame.
|
||||
uint32_t pad[1]; // padding for later use
|
||||
};
|
||||
|
||||
// Sets the (non-animated and non-fragmented) image in the mux object.
|
||||
// Note: Any existing images (including frames/fragments) will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object in which the image is to be set
|
||||
// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
|
||||
// WebP file (non-animated and non-fragmented)
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(
|
||||
WebPMux* mux, const WebPData* bitstream, int copy_data);
|
||||
|
||||
// Adds a frame at the end of the mux object.
|
||||
// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM
|
||||
// (2) For setting a non-animated non-fragmented image, use
|
||||
// WebPMuxSetImage() instead.
|
||||
// (3) Type of frame being pushed must be same as the frames in mux.
|
||||
// (4) As WebP only supports even offsets, any odd offset will be snapped
|
||||
// to an even location using: offset &= ~1
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which the frame is to be added
|
||||
// frame - (in) frame data.
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
|
||||
// or if content of 'frame' is invalid.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame(
|
||||
WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data);
|
||||
|
||||
// Gets the nth frame from the mux object.
|
||||
// The content of 'frame->bitstream' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear().
|
||||
// nth=0 has a special meaning - last position.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the info is to be fetched
|
||||
// nth - (in) index of the frame in the mux object
|
||||
// frame - (out) data of the returned frame
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object.
|
||||
// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame(
|
||||
const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame);
|
||||
|
||||
// Deletes a frame from the mux object.
|
||||
// nth=0 has a special meaning - last position.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which a frame is to be deleted
|
||||
// nth - (in) The position from which the frame is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object
|
||||
// before deletion.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Animation.
|
||||
|
||||
// Animation parameters.
|
||||
struct WebPMuxAnimParams {
|
||||
uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as:
|
||||
// Bits 00 to 07: Alpha.
|
||||
// Bits 08 to 15: Red.
|
||||
// Bits 16 to 23: Green.
|
||||
// Bits 24 to 31: Blue.
|
||||
int loop_count; // Number of times to repeat the animation [0 = infinite].
|
||||
};
|
||||
|
||||
// Sets the animation parameters in the mux object. Any existing ANIM chunks
|
||||
// will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object in which ANIM chunk is to be set/added
|
||||
// params - (in) animation parameters.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetAnimationParams(
|
||||
WebPMux* mux, const WebPMuxAnimParams* params);
|
||||
|
||||
// Gets the animation parameters from the mux object.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the animation parameters to be fetched
|
||||
// params - (out) animation parameters extracted from the ANIM chunk
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
|
||||
const WebPMux* mux, WebPMuxAnimParams* params);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc Utilities.
|
||||
|
||||
// Sets the canvas size for the mux object. The width and height can be
|
||||
// specified explicitly or left as zero (0, 0).
|
||||
// * When width and height are specified explicitly, then this frame bound is
|
||||
// enforced during subsequent calls to WebPMuxAssemble() and an error is
|
||||
// reported if any animated frame does not completely fit within the canvas.
|
||||
// * When unspecified (0, 0), the constructed canvas will get the frame bounds
|
||||
// from the bounding-box over all frames after calling WebPMuxAssemble().
|
||||
// Parameters:
|
||||
// mux - (in) object to which the canvas size is to be set
|
||||
// width - (in) canvas width
|
||||
// height - (in) canvas height
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
|
||||
// width or height are invalid or out of bounds
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetCanvasSize(WebPMux* mux,
|
||||
int width, int height);
|
||||
|
||||
// Gets the canvas size from the mux object.
|
||||
// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
|
||||
// That is, the mux object hasn't been modified since the last call to
|
||||
// WebPMuxAssemble() or WebPMuxCreate().
|
||||
// Parameters:
|
||||
// mux - (in) object from which the canvas size is to be fetched
|
||||
// width - (out) canvas width
|
||||
// height - (out) canvas height
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL.
|
||||
// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetCanvasSize(const WebPMux* mux,
|
||||
int* width, int* height);
|
||||
|
||||
// Gets the feature flags from the mux object.
|
||||
// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
|
||||
// That is, the mux object hasn't been modified since the last call to
|
||||
// WebPMuxAssemble() or WebPMuxCreate().
|
||||
// Parameters:
|
||||
// mux - (in) object from which the features are to be fetched
|
||||
// flags - (out) the flags specifying which features are present in the
|
||||
// mux object. This will be an OR of various flag values.
|
||||
// Enum 'WebPFeatureFlags' can be used to test individual flag values.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL.
|
||||
// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetFeatures(const WebPMux* mux,
|
||||
uint32_t* flags);
|
||||
|
||||
// Gets number of chunks with the given 'id' in the mux object.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the info is to be fetched
|
||||
// id - (in) chunk id specifying the type of chunk
|
||||
// num_elements - (out) number of chunks with the given chunk id
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
|
||||
WebPChunkId id, int* num_elements);
|
||||
|
||||
// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'.
|
||||
// This function also validates the mux object.
|
||||
// Note: The content of 'assembled_data' will be ignored and overwritten.
|
||||
// Also, the content of 'assembled_data' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear(). It's always safe to call WebPDataClear() upon return,
|
||||
// even in case of error.
|
||||
// Parameters:
|
||||
// mux - (in/out) object whose chunks are to be assembled
|
||||
// assembled_data - (out) assembled WebP data
|
||||
// Returns:
|
||||
// WEBP_MUX_BAD_DATA - if mux object is invalid.
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxAssemble(WebPMux* mux,
|
||||
WebPData* assembled_data);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPAnimEncoder API
|
||||
//
|
||||
// This API allows encoding (possibly) animated WebP images.
|
||||
//
|
||||
// Code Example:
|
||||
/*
|
||||
WebPAnimEncoderOptions enc_options;
|
||||
WebPAnimEncoderOptionsInit(&enc_options);
|
||||
// Tune 'enc_options' as needed.
|
||||
WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
|
||||
while(<there are more frames>) {
|
||||
WebPConfig config;
|
||||
WebPConfigInit(&config);
|
||||
// Tune 'config' as needed.
|
||||
WebPAnimEncoderAdd(enc, frame, timestamp_ms, &config);
|
||||
}
|
||||
WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
|
||||
WebPAnimEncoderAssemble(enc, webp_data);
|
||||
WebPAnimEncoderDelete(enc);
|
||||
// Write the 'webp_data' to a file, or re-mux it further.
|
||||
*/
|
||||
|
||||
typedef struct WebPAnimEncoder WebPAnimEncoder; // Main opaque object.
|
||||
|
||||
// Forward declarations. Defined in encode.h.
|
||||
struct WebPPicture;
|
||||
struct WebPConfig;
|
||||
|
||||
// Global options.
|
||||
struct WebPAnimEncoderOptions {
|
||||
WebPMuxAnimParams anim_params; // Animation parameters.
|
||||
int minimize_size; // If true, minimize the output size (slow). Implicitly
|
||||
// disables key-frame insertion.
|
||||
int kmin;
|
||||
int kmax; // Minimum and maximum distance between consecutive key
|
||||
// frames in the output. The library may insert some key
|
||||
// frames as needed to satisfy this criteria.
|
||||
// Note that these conditions should hold: kmax > kmin
|
||||
// and kmin >= kmax / 2 + 1. Also, if kmin == 0, then
|
||||
// key-frame insertion is disabled; and if kmax == 0,
|
||||
// then all frames will be key-frames.
|
||||
int allow_mixed; // If true, use mixed compression mode; may choose
|
||||
// either lossy and lossless for each frame.
|
||||
int verbose; // If true, print info and warning messages to stderr.
|
||||
|
||||
uint32_t padding[4]; // Padding for later use.
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderOptionsInitInternal(
|
||||
WebPAnimEncoderOptions*, int);
|
||||
|
||||
// Should always be called, to initialize a fresh WebPAnimEncoderOptions
|
||||
// structure before modification. Returns false in case of version mismatch.
|
||||
// WebPAnimEncoderOptionsInit() must have succeeded before using the
|
||||
// 'enc_options' object.
|
||||
static WEBP_INLINE int WebPAnimEncoderOptionsInit(
|
||||
WebPAnimEncoderOptions* enc_options) {
|
||||
return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(WebPAnimEncoder*) WebPAnimEncoderNewInternal(
|
||||
int, int, const WebPAnimEncoderOptions*, int);
|
||||
|
||||
// Creates and initializes a WebPAnimEncoder object.
|
||||
// Parameters:
|
||||
// width/height - (in) canvas width and height of the animation.
|
||||
// enc_options - (in) encoding options; can be passed NULL to pick
|
||||
// reasonable defaults.
|
||||
// Returns:
|
||||
// A pointer to the newly created WebPAnimEncoder object.
|
||||
// Or NULL in case of memory error.
|
||||
static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
|
||||
int width, int height, const WebPAnimEncoderOptions* enc_options) {
|
||||
return WebPAnimEncoderNewInternal(width, height, enc_options,
|
||||
WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Optimize the given frame for WebP, encode it and add it to the
|
||||
// WebPAnimEncoder object.
|
||||
// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which
|
||||
// indicates that no more frames are to be added. This call is also used to
|
||||
// determine the duration of the last frame.
|
||||
// Parameters:
|
||||
// enc - (in/out) object to which the frame is to be added.
|
||||
// frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A)
|
||||
// format, it will be converted to ARGB, which incurs a small loss.
|
||||
// timestamp_ms - (in) timestamp of this frame in milliseconds.
|
||||
// Duration of a frame would be calculated as
|
||||
// "timestamp of next frame - timestamp of this frame".
|
||||
// Hence, timestamps should be in non-decreasing order.
|
||||
// config - (in) encoding options; can be passed NULL to pick
|
||||
// reasonable defaults.
|
||||
// Returns:
|
||||
// On error, returns false and frame->error_code is set appropriately.
|
||||
// Otherwise, returns true.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderAdd(
|
||||
WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
|
||||
const struct WebPConfig* config);
|
||||
|
||||
// Assemble all frames added so far into a WebP bitstream.
|
||||
// This call should be preceded by a call to 'WebPAnimEncoderAdd' with
|
||||
// frame = NULL; if not, the duration of the last frame will be internally
|
||||
// estimated.
|
||||
// Parameters:
|
||||
// enc - (in/out) object from which the frames are to be assembled.
|
||||
// webp_data - (out) generated WebP bitstream.
|
||||
// Returns:
|
||||
// True on success.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
|
||||
WebPData* webp_data);
|
||||
|
||||
// Get error string corresponding to the most recent call using 'enc'. The
|
||||
// returned string is owned by 'enc' and is valid only until the next call to
|
||||
// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete().
|
||||
// Parameters:
|
||||
// enc - (in/out) object from which the error string is to be fetched.
|
||||
// Returns:
|
||||
// NULL if 'enc' is NULL. Otherwise, returns the error string if the last call
|
||||
// to 'enc' had an error, or an empty string if the last call was a success.
|
||||
WEBP_EXTERN(const char*) WebPAnimEncoderGetError(WebPAnimEncoder* enc);
|
||||
|
||||
// Deletes the WebPAnimEncoder object.
|
||||
// Parameters:
|
||||
// enc - (in/out) object to be deleted
|
||||
WEBP_EXTERN(void) WebPAnimEncoderDelete(WebPAnimEncoder* enc);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_MUX_H_ */
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Data-types common to the mux and demux libraries.
|
||||
//
|
||||
// Author: Urvang (urvang@google.com)
|
||||
|
||||
#ifndef WEBP_WEBP_MUX_TYPES_H_
|
||||
#define WEBP_WEBP_MUX_TYPES_H_
|
||||
|
||||
#include <stdlib.h> // free()
|
||||
#include <string.h> // memset()
|
||||
#include "./types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPFeatureFlags WebPFeatureFlags;
|
||||
// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose;
|
||||
// typedef enum WebPMuxAnimBlend WebPMuxAnimBlend;
|
||||
typedef struct WebPData WebPData;
|
||||
|
||||
// VP8X Feature Flags.
|
||||
typedef enum WebPFeatureFlags {
|
||||
FRAGMENTS_FLAG = 0x00000001,
|
||||
ANIMATION_FLAG = 0x00000002,
|
||||
XMP_FLAG = 0x00000004,
|
||||
EXIF_FLAG = 0x00000008,
|
||||
ALPHA_FLAG = 0x00000010,
|
||||
ICCP_FLAG = 0x00000020
|
||||
} WebPFeatureFlags;
|
||||
|
||||
// Dispose method (animation only). Indicates how the area used by the current
|
||||
// frame is to be treated before rendering the next frame on the canvas.
|
||||
typedef enum WebPMuxAnimDispose {
|
||||
WEBP_MUX_DISPOSE_NONE, // Do not dispose.
|
||||
WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color.
|
||||
} WebPMuxAnimDispose;
|
||||
|
||||
// Blend operation (animation only). Indicates how transparent pixels of the
|
||||
// current frame are blended with those of the previous canvas.
|
||||
typedef enum WebPMuxAnimBlend {
|
||||
WEBP_MUX_BLEND, // Blend.
|
||||
WEBP_MUX_NO_BLEND // Do not blend.
|
||||
} WebPMuxAnimBlend;
|
||||
|
||||
// Data type used to describe 'raw' data, e.g., chunk data
|
||||
// (ICC profile, metadata) and WebP compressed image data.
|
||||
struct WebPData {
|
||||
const uint8_t* bytes;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Initializes the contents of the 'webp_data' object with default values.
|
||||
static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
|
||||
if (webp_data != NULL) {
|
||||
memset(webp_data, 0, sizeof(*webp_data));
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the contents of the 'webp_data' object by calling free(). Does not
|
||||
// deallocate the object itself.
|
||||
static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
|
||||
if (webp_data != NULL) {
|
||||
free((void*)webp_data->bytes);
|
||||
WebPDataInit(webp_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocates necessary storage for 'dst' and copies the contents of 'src'.
|
||||
// Returns true on success.
|
||||
static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
|
||||
if (src == NULL || dst == NULL) return 0;
|
||||
WebPDataInit(dst);
|
||||
if (src->bytes != NULL && src->size != 0) {
|
||||
dst->bytes = (uint8_t*)malloc(src->size);
|
||||
if (dst->bytes == NULL) return 0;
|
||||
memcpy((void*)dst->bytes, src->bytes, src->size);
|
||||
dst->size = src->size;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_MUX_TYPES_H_ */
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Common types
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_WEBP_TYPES_H_
|
||||
#define WEBP_WEBP_TYPES_H_
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <inttypes.h>
|
||||
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
|
||||
#define WEBP_INLINE inline
|
||||
#else
|
||||
#define WEBP_INLINE
|
||||
#endif
|
||||
#else
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long int uint64_t;
|
||||
typedef long long int int64_t;
|
||||
#define WEBP_INLINE __forceinline
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifndef WEBP_EXTERN
|
||||
// This explicitly marks library functions and allows for changing the
|
||||
// signature for e.g., Windows DLL builds.
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define WEBP_EXTERN(type) extern __attribute__ ((visibility ("default"))) type
|
||||
# else
|
||||
# define WEBP_EXTERN(type) extern type
|
||||
# endif /* __GNUC__ >= 4 */
|
||||
#endif /* WEBP_EXTERN */
|
||||
|
||||
// Macro to check ABI compatibility (same major revision number)
|
||||
#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8))
|
||||
|
||||
#endif /* WEBP_WEBP_TYPES_H_ */
|
Binary file not shown.
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// YYAnimatedImageView.h
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/10/19.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An image view for displaying animated image.
|
||||
|
||||
@discussion It is a fully compatible `UIImageView` subclass.
|
||||
If the `image` or `highlightedImage` property adopt to the `YYAnimatedImage` protocol,
|
||||
then it can be used to play the multi-frame animation. The animation can also be
|
||||
controlled with the UIImageView methods `-startAnimating`, `-stopAnimating` and `-isAnimating`.
|
||||
|
||||
This view request the frame data just in time. When the device has enough free memory,
|
||||
this view may cache some or all future frames in an inner buffer for lower CPU cost.
|
||||
Buffer size is dynamically adjusted based on the current state of the device memory.
|
||||
|
||||
Sample Code:
|
||||
|
||||
// ani@3x.gif
|
||||
YYImage *image = [YYImage imageNamed:@"ani"];
|
||||
YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
|
||||
[view addSubView:imageView];
|
||||
*/
|
||||
@interface YYAnimatedImageView : UIImageView
|
||||
|
||||
/**
|
||||
If the image has more than one frame, set this value to `YES` will automatically
|
||||
play/stop the animation when the view become visible/invisible.
|
||||
|
||||
The default value is `YES`.
|
||||
*/
|
||||
@property (nonatomic) BOOL autoPlayAnimatedImage;
|
||||
|
||||
/**
|
||||
Index of the currently displayed frame (index from 0).
|
||||
|
||||
Set a new value to this property will cause to display the new frame immediately.
|
||||
If the new value is invalid, this method has no effect.
|
||||
|
||||
You can add an observer to this property to observe the playing status.
|
||||
*/
|
||||
@property (nonatomic) NSUInteger currentAnimatedImageIndex;
|
||||
|
||||
/**
|
||||
Whether the image view is playing animation currently.
|
||||
|
||||
You can add an observer to this property to observe the playing status.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL currentIsPlayingAnimation;
|
||||
|
||||
/**
|
||||
The animation timer's runloop mode, default is `NSRunLoopCommonModes`.
|
||||
|
||||
Set this property to `NSDefaultRunLoopMode` will make the animation pause during
|
||||
UIScrollView scrolling.
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *runloopMode;
|
||||
|
||||
/**
|
||||
The max size (in bytes) for inner frame buffer size, default is 0 (dynamically).
|
||||
|
||||
When the device has enough free memory, this view will request and decode some or
|
||||
all future frame image into an inner buffer. If this property's value is 0, then
|
||||
the max buffer size will be dynamically adjusted based on the current state of
|
||||
the device free memory. Otherwise, the buffer size will be limited by this value.
|
||||
|
||||
When receive memory warning or app enter background, the buffer will be released
|
||||
immediately, and may grow back at the right time.
|
||||
*/
|
||||
@property (nonatomic) NSUInteger maxBufferSize;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The YYAnimatedImage protocol declares the required methods for animated image
|
||||
display with YYAnimatedImageView.
|
||||
|
||||
Subclass a UIImage and implement this protocol, so that instances of that class
|
||||
can be set to YYAnimatedImageView.image or YYAnimatedImageView.highlightedImage
|
||||
to display animation.
|
||||
|
||||
See `YYImage` and `YYFrameImage` for example.
|
||||
*/
|
||||
@protocol YYAnimatedImage <NSObject>
|
||||
@required
|
||||
/// Total animated frame count.
|
||||
/// It the frame count is less than 1, then the methods below will be ignored.
|
||||
- (NSUInteger)animatedImageFrameCount;
|
||||
|
||||
/// Animation loop count, 0 means infinite looping.
|
||||
- (NSUInteger)animatedImageLoopCount;
|
||||
|
||||
/// Bytes per frame (in memory). It may used to optimize memory buffer size.
|
||||
- (NSUInteger)animatedImageBytesPerFrame;
|
||||
|
||||
/// Returns the frame image from a specified index.
|
||||
/// This method may be called on background thread.
|
||||
/// @param index Frame index (zero based).
|
||||
- (nullable UIImage *)animatedImageFrameAtIndex:(NSUInteger)index;
|
||||
|
||||
/// Returns the frames's duration from a specified index.
|
||||
/// @param index Frame index (zero based).
|
||||
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index;
|
||||
|
||||
@optional
|
||||
/// A rectangle in image coordinates defining the subrectangle of the image that
|
||||
/// will be displayed. The rectangle should not outside the image's bounds.
|
||||
/// It may used to display sprite animation with a single image (sprite sheet).
|
||||
- (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,672 @@
|
|||
//
|
||||
// YYAnimatedImageView.m
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/10/19.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYAnimatedImageView.h"
|
||||
#import "YYImageCoder.h"
|
||||
#import <pthread.h>
|
||||
#import <mach/mach.h>
|
||||
|
||||
|
||||
#define BUFFER_SIZE (10 * 1024 * 1024) // 10MB (minimum memory buffer size)
|
||||
|
||||
#define LOCK(...) dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER); \
|
||||
__VA_ARGS__; \
|
||||
dispatch_semaphore_signal(self->_lock);
|
||||
|
||||
#define LOCK_VIEW(...) dispatch_semaphore_wait(view->_lock, DISPATCH_TIME_FOREVER); \
|
||||
__VA_ARGS__; \
|
||||
dispatch_semaphore_signal(view->_lock);
|
||||
|
||||
|
||||
static int64_t _YYDeviceMemoryTotal() {
|
||||
int64_t mem = [[NSProcessInfo processInfo] physicalMemory];
|
||||
if (mem < -1) mem = -1;
|
||||
return mem;
|
||||
}
|
||||
|
||||
static int64_t _YYDeviceMemoryFree() {
|
||||
mach_port_t host_port = mach_host_self();
|
||||
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
||||
vm_size_t page_size;
|
||||
vm_statistics_data_t vm_stat;
|
||||
kern_return_t kern;
|
||||
|
||||
kern = host_page_size(host_port, &page_size);
|
||||
if (kern != KERN_SUCCESS) return -1;
|
||||
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
|
||||
if (kern != KERN_SUCCESS) return -1;
|
||||
return vm_stat.free_count * page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
A proxy used to hold a weak object.
|
||||
It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink.
|
||||
*/
|
||||
@interface _YYImageWeakProxy : NSProxy
|
||||
@property (nonatomic, weak, readonly) id target;
|
||||
- (instancetype)initWithTarget:(id)target;
|
||||
+ (instancetype)proxyWithTarget:(id)target;
|
||||
@end
|
||||
|
||||
@implementation _YYImageWeakProxy
|
||||
- (instancetype)initWithTarget:(id)target {
|
||||
_target = target;
|
||||
return self;
|
||||
}
|
||||
+ (instancetype)proxyWithTarget:(id)target {
|
||||
return [[_YYImageWeakProxy alloc] initWithTarget:target];
|
||||
}
|
||||
- (id)forwardingTargetForSelector:(SEL)selector {
|
||||
return _target;
|
||||
}
|
||||
- (void)forwardInvocation:(NSInvocation *)invocation {
|
||||
void *null = NULL;
|
||||
[invocation setReturnValue:&null];
|
||||
}
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
|
||||
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
|
||||
}
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector {
|
||||
return [_target respondsToSelector:aSelector];
|
||||
}
|
||||
- (BOOL)isEqual:(id)object {
|
||||
return [_target isEqual:object];
|
||||
}
|
||||
- (NSUInteger)hash {
|
||||
return [_target hash];
|
||||
}
|
||||
- (Class)superclass {
|
||||
return [_target superclass];
|
||||
}
|
||||
- (Class)class {
|
||||
return [_target class];
|
||||
}
|
||||
- (BOOL)isKindOfClass:(Class)aClass {
|
||||
return [_target isKindOfClass:aClass];
|
||||
}
|
||||
- (BOOL)isMemberOfClass:(Class)aClass {
|
||||
return [_target isMemberOfClass:aClass];
|
||||
}
|
||||
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
|
||||
return [_target conformsToProtocol:aProtocol];
|
||||
}
|
||||
- (BOOL)isProxy {
|
||||
return YES;
|
||||
}
|
||||
- (NSString *)description {
|
||||
return [_target description];
|
||||
}
|
||||
- (NSString *)debugDescription {
|
||||
return [_target debugDescription];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
|
||||
YYAnimatedImageTypeNone = 0,
|
||||
YYAnimatedImageTypeImage,
|
||||
YYAnimatedImageTypeHighlightedImage,
|
||||
YYAnimatedImageTypeImages,
|
||||
YYAnimatedImageTypeHighlightedImages,
|
||||
};
|
||||
|
||||
@interface YYAnimatedImageView() {
|
||||
@package
|
||||
UIImage <YYAnimatedImage> *_curAnimatedImage;
|
||||
|
||||
dispatch_once_t _onceToken;
|
||||
dispatch_semaphore_t _lock; ///< lock for _buffer
|
||||
NSOperationQueue *_requestQueue; ///< image request queue, serial
|
||||
|
||||
CADisplayLink *_link; ///< ticker for change frame
|
||||
NSTimeInterval _time; ///< time after last frame
|
||||
|
||||
UIImage *_curFrame; ///< current frame to display
|
||||
NSUInteger _curIndex; ///< current frame index (from 0)
|
||||
NSUInteger _totalFrameCount; ///< total frame count
|
||||
|
||||
BOOL _loopEnd; ///< whether the loop is end.
|
||||
NSUInteger _curLoop; ///< current loop count (from 0)
|
||||
NSUInteger _totalLoop; ///< total loop count, 0 means infinity
|
||||
|
||||
NSMutableDictionary *_buffer; ///< frame buffer
|
||||
BOOL _bufferMiss; ///< whether miss frame on last opportunity
|
||||
NSUInteger _maxBufferCount; ///< maximum buffer count
|
||||
NSInteger _incrBufferCount; ///< current allowed buffer count (will increase by step)
|
||||
|
||||
CGRect _curContentsRect;
|
||||
BOOL _curImageHasContentsRect; ///< image has implementated "animatedImageContentsRectAtIndex:"
|
||||
}
|
||||
@property (nonatomic, readwrite) BOOL currentIsPlayingAnimation;
|
||||
- (void)calcMaxBufferCount;
|
||||
@end
|
||||
|
||||
/// An operation for image fetch
|
||||
@interface _YYAnimatedImageViewFetchOperation : NSOperation
|
||||
@property (nonatomic, weak) YYAnimatedImageView *view;
|
||||
@property (nonatomic, assign) NSUInteger nextIndex;
|
||||
@property (nonatomic, strong) UIImage <YYAnimatedImage> *curImage;
|
||||
@end
|
||||
|
||||
@implementation _YYAnimatedImageViewFetchOperation
|
||||
- (void)main {
|
||||
__strong YYAnimatedImageView *view = _view;
|
||||
if (!view) return;
|
||||
if ([self isCancelled]) return;
|
||||
view->_incrBufferCount++;
|
||||
if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
|
||||
if (view->_incrBufferCount > (NSInteger)view->_maxBufferCount) {
|
||||
view->_incrBufferCount = view->_maxBufferCount;
|
||||
}
|
||||
NSUInteger idx = _nextIndex;
|
||||
NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
|
||||
NSUInteger total = view->_totalFrameCount;
|
||||
view = nil;
|
||||
|
||||
for (int i = 0; i < max; i++, idx++) {
|
||||
@autoreleasepool {
|
||||
if (idx >= total) idx = 0;
|
||||
if ([self isCancelled]) break;
|
||||
__strong YYAnimatedImageView *view = _view;
|
||||
if (!view) break;
|
||||
LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
|
||||
|
||||
if (miss) {
|
||||
UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
|
||||
img = img.yy_imageByDecoded;
|
||||
if ([self isCancelled]) break;
|
||||
LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
|
||||
view = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation YYAnimatedImageView
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_runloopMode = NSRunLoopCommonModes;
|
||||
_autoPlayAnimatedImage = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
_runloopMode = NSRunLoopCommonModes;
|
||||
_autoPlayAnimatedImage = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image {
|
||||
self = [super init];
|
||||
_runloopMode = NSRunLoopCommonModes;
|
||||
_autoPlayAnimatedImage = YES;
|
||||
self.frame = (CGRect) {CGPointZero, image.size };
|
||||
self.image = image;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage {
|
||||
self = [super init];
|
||||
_runloopMode = NSRunLoopCommonModes;
|
||||
_autoPlayAnimatedImage = YES;
|
||||
CGSize size = image ? image.size : highlightedImage.size;
|
||||
self.frame = (CGRect) {CGPointZero, size };
|
||||
self.image = image;
|
||||
self.highlightedImage = highlightedImage;
|
||||
return self;
|
||||
}
|
||||
|
||||
// init the animated params.
|
||||
- (void)resetAnimated {
|
||||
dispatch_once(&_onceToken, ^{
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
_buffer = [NSMutableDictionary new];
|
||||
_requestQueue = [[NSOperationQueue alloc] init];
|
||||
_requestQueue.maxConcurrentOperationCount = 1;
|
||||
_link = [CADisplayLink displayLinkWithTarget:[_YYImageWeakProxy proxyWithTarget:self] selector:@selector(step:)];
|
||||
if (_runloopMode) {
|
||||
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
|
||||
}
|
||||
_link.paused = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
});
|
||||
|
||||
[_requestQueue cancelAllOperations];
|
||||
LOCK(
|
||||
if (_buffer.count) {
|
||||
NSMutableDictionary *holder = _buffer;
|
||||
_buffer = [NSMutableDictionary new];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
// Capture the dictionary to global queue,
|
||||
// release these images in background to avoid blocking UI thread.
|
||||
[holder class];
|
||||
});
|
||||
}
|
||||
);
|
||||
_link.paused = YES;
|
||||
_time = 0;
|
||||
if (_curIndex != 0) {
|
||||
[self willChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
_curIndex = 0;
|
||||
[self didChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
}
|
||||
_curAnimatedImage = nil;
|
||||
_curFrame = nil;
|
||||
_curLoop = 0;
|
||||
_totalLoop = 0;
|
||||
_totalFrameCount = 1;
|
||||
_loopEnd = NO;
|
||||
_bufferMiss = NO;
|
||||
_incrBufferCount = 0;
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image {
|
||||
if (self.image == image) return;
|
||||
[self setImage:image withType:YYAnimatedImageTypeImage];
|
||||
}
|
||||
|
||||
- (void)setHighlightedImage:(UIImage *)highlightedImage {
|
||||
if (self.highlightedImage == highlightedImage) return;
|
||||
[self setImage:highlightedImage withType:YYAnimatedImageTypeHighlightedImage];
|
||||
}
|
||||
|
||||
- (void)setAnimationImages:(NSArray *)animationImages {
|
||||
if (self.animationImages == animationImages) return;
|
||||
[self setImage:animationImages withType:YYAnimatedImageTypeImages];
|
||||
}
|
||||
|
||||
- (void)setHighlightedAnimationImages:(NSArray *)highlightedAnimationImages {
|
||||
if (self.highlightedAnimationImages == highlightedAnimationImages) return;
|
||||
[self setImage:highlightedAnimationImages withType:YYAnimatedImageTypeHighlightedImages];
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted {
|
||||
[super setHighlighted:highlighted];
|
||||
if (_link) [self resetAnimated];
|
||||
[self imageChanged];
|
||||
}
|
||||
|
||||
- (id)imageForType:(YYAnimatedImageType)type {
|
||||
switch (type) {
|
||||
case YYAnimatedImageTypeNone: return nil;
|
||||
case YYAnimatedImageTypeImage: return self.image;
|
||||
case YYAnimatedImageTypeHighlightedImage: return self.highlightedImage;
|
||||
case YYAnimatedImageTypeImages: return self.animationImages;
|
||||
case YYAnimatedImageTypeHighlightedImages: return self.highlightedAnimationImages;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (YYAnimatedImageType)currentImageType {
|
||||
YYAnimatedImageType curType = YYAnimatedImageTypeNone;
|
||||
if (self.highlighted) {
|
||||
if (self.highlightedAnimationImages.count) curType = YYAnimatedImageTypeHighlightedImages;
|
||||
else if (self.highlightedImage) curType = YYAnimatedImageTypeHighlightedImage;
|
||||
}
|
||||
if (curType == YYAnimatedImageTypeNone) {
|
||||
if (self.animationImages.count) curType = YYAnimatedImageTypeImages;
|
||||
else if (self.image) curType = YYAnimatedImageTypeImage;
|
||||
}
|
||||
return curType;
|
||||
}
|
||||
|
||||
- (void)setImage:(id)image withType:(YYAnimatedImageType)type {
|
||||
[self stopAnimating];
|
||||
if (_link) [self resetAnimated];
|
||||
_curFrame = nil;
|
||||
switch (type) {
|
||||
case YYAnimatedImageTypeNone: break;
|
||||
case YYAnimatedImageTypeImage: super.image = image; break;
|
||||
case YYAnimatedImageTypeHighlightedImage: super.highlightedImage = image; break;
|
||||
case YYAnimatedImageTypeImages: super.animationImages = image; break;
|
||||
case YYAnimatedImageTypeHighlightedImages: super.highlightedAnimationImages = image; break;
|
||||
}
|
||||
[self imageChanged];
|
||||
}
|
||||
|
||||
- (void)imageChanged {
|
||||
YYAnimatedImageType newType = [self currentImageType];
|
||||
id newVisibleImage = [self imageForType:newType];
|
||||
NSUInteger newImageFrameCount = 0;
|
||||
BOOL hasContentsRect = NO;
|
||||
if ([newVisibleImage isKindOfClass:[UIImage class]] &&
|
||||
[newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)]) {
|
||||
newImageFrameCount = ((UIImage<YYAnimatedImage> *) newVisibleImage).animatedImageFrameCount;
|
||||
if (newImageFrameCount > 1) {
|
||||
hasContentsRect = [((UIImage<YYAnimatedImage> *) newVisibleImage) respondsToSelector:@selector(animatedImageContentsRectAtIndex:)];
|
||||
}
|
||||
}
|
||||
if (!hasContentsRect && _curImageHasContentsRect) {
|
||||
if (!CGRectEqualToRect(self.layer.contentsRect, CGRectMake(0, 0, 1, 1)) ) {
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
|
||||
[CATransaction commit];
|
||||
}
|
||||
}
|
||||
_curImageHasContentsRect = hasContentsRect;
|
||||
if (hasContentsRect) {
|
||||
CGRect rect = [((UIImage<YYAnimatedImage> *) newVisibleImage) animatedImageContentsRectAtIndex:0];
|
||||
[self setContentsRect:rect forImage:newVisibleImage];
|
||||
}
|
||||
|
||||
if (newImageFrameCount > 1) {
|
||||
[self resetAnimated];
|
||||
_curAnimatedImage = newVisibleImage;
|
||||
_curFrame = newVisibleImage;
|
||||
_totalLoop = _curAnimatedImage.animatedImageLoopCount;
|
||||
_totalFrameCount = _curAnimatedImage.animatedImageFrameCount;
|
||||
[self calcMaxBufferCount];
|
||||
}
|
||||
[self setNeedsDisplay];
|
||||
[self didMoved];
|
||||
}
|
||||
|
||||
// dynamically adjust buffer size for current memory.
|
||||
- (void)calcMaxBufferCount {
|
||||
int64_t bytes = (int64_t)_curAnimatedImage.animatedImageBytesPerFrame;
|
||||
if (bytes == 0) bytes = 1024;
|
||||
|
||||
int64_t total = _YYDeviceMemoryTotal();
|
||||
int64_t free = _YYDeviceMemoryFree();
|
||||
int64_t max = MIN(total * 0.2, free * 0.6);
|
||||
max = MAX(max, BUFFER_SIZE);
|
||||
if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
|
||||
double maxBufferCount = (double)max / (double)bytes;
|
||||
if (maxBufferCount < 1) maxBufferCount = 1;
|
||||
else if (maxBufferCount > 512) maxBufferCount = 512;
|
||||
_maxBufferCount = maxBufferCount;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_requestQueue cancelAllOperations];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[_link invalidate];
|
||||
}
|
||||
|
||||
- (BOOL)isAnimating {
|
||||
return self.currentIsPlayingAnimation;
|
||||
}
|
||||
|
||||
- (void)stopAnimating {
|
||||
[super stopAnimating];
|
||||
[_requestQueue cancelAllOperations];
|
||||
_link.paused = YES;
|
||||
self.currentIsPlayingAnimation = NO;
|
||||
}
|
||||
|
||||
- (void)startAnimating {
|
||||
YYAnimatedImageType type = [self currentImageType];
|
||||
if (type == YYAnimatedImageTypeImages || type == YYAnimatedImageTypeHighlightedImages) {
|
||||
NSArray *images = [self imageForType:type];
|
||||
if (images.count > 0) {
|
||||
[super startAnimating];
|
||||
self.currentIsPlayingAnimation = YES;
|
||||
}
|
||||
} else {
|
||||
if (_curAnimatedImage && _link.paused) {
|
||||
_curLoop = 0;
|
||||
_loopEnd = NO;
|
||||
_link.paused = NO;
|
||||
self.currentIsPlayingAnimation = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
|
||||
[_requestQueue cancelAllOperations];
|
||||
[_requestQueue addOperationWithBlock: ^{
|
||||
_incrBufferCount = -60 - (int)(arc4random() % 120); // about 1~3 seconds to grow back..
|
||||
NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
|
||||
LOCK(
|
||||
NSArray * keys = _buffer.allKeys;
|
||||
for (NSNumber * key in keys) {
|
||||
if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
|
||||
[_buffer removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
)//LOCK
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)didEnterBackground:(NSNotification *)notification {
|
||||
[_requestQueue cancelAllOperations];
|
||||
NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
|
||||
LOCK(
|
||||
NSArray * keys = _buffer.allKeys;
|
||||
for (NSNumber * key in keys) {
|
||||
if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
|
||||
[_buffer removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
)//LOCK
|
||||
}
|
||||
|
||||
- (void)step:(CADisplayLink *)link {
|
||||
UIImage <YYAnimatedImage> *image = _curAnimatedImage;
|
||||
NSMutableDictionary *buffer = _buffer;
|
||||
UIImage *bufferedImage = nil;
|
||||
NSUInteger nextIndex = (_curIndex + 1) % _totalFrameCount;
|
||||
BOOL bufferIsFull = NO;
|
||||
|
||||
if (!image) return;
|
||||
if (_loopEnd) { // view will keep in last frame
|
||||
[self stopAnimating];
|
||||
return;
|
||||
}
|
||||
|
||||
NSTimeInterval delay = 0;
|
||||
if (!_bufferMiss) {
|
||||
_time += link.duration;
|
||||
delay = [image animatedImageDurationAtIndex:_curIndex];
|
||||
if (_time < delay) return;
|
||||
_time -= delay;
|
||||
if (nextIndex == 0) {
|
||||
_curLoop++;
|
||||
if (_curLoop >= _totalLoop && _totalLoop != 0) {
|
||||
_loopEnd = YES;
|
||||
[self stopAnimating];
|
||||
[self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
|
||||
return; // stop at last frame
|
||||
}
|
||||
}
|
||||
delay = [image animatedImageDurationAtIndex:nextIndex];
|
||||
if (_time > delay) _time = delay; // do not jump over frame
|
||||
}
|
||||
LOCK(
|
||||
bufferedImage = buffer[@(nextIndex)];
|
||||
if (bufferedImage) {
|
||||
if ((int)_incrBufferCount < _totalFrameCount) {
|
||||
[buffer removeObjectForKey:@(nextIndex)];
|
||||
}
|
||||
[self willChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
_curIndex = nextIndex;
|
||||
[self didChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
_curFrame = bufferedImage == (id)[NSNull null] ? nil : bufferedImage;
|
||||
if (_curImageHasContentsRect) {
|
||||
_curContentsRect = [image animatedImageContentsRectAtIndex:_curIndex];
|
||||
[self setContentsRect:_curContentsRect forImage:_curFrame];
|
||||
}
|
||||
nextIndex = (_curIndex + 1) % _totalFrameCount;
|
||||
_bufferMiss = NO;
|
||||
if (buffer.count == _totalFrameCount) {
|
||||
bufferIsFull = YES;
|
||||
}
|
||||
} else {
|
||||
_bufferMiss = YES;
|
||||
}
|
||||
)//LOCK
|
||||
|
||||
if (!_bufferMiss) {
|
||||
[self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
|
||||
}
|
||||
|
||||
if (!bufferIsFull && _requestQueue.operationCount == 0) { // if some work not finished, wait for next opportunity
|
||||
_YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
|
||||
operation.view = self;
|
||||
operation.nextIndex = nextIndex;
|
||||
operation.curImage = image;
|
||||
[_requestQueue addOperation:operation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)displayLayer:(CALayer *)layer {
|
||||
if (_curFrame) {
|
||||
layer.contents = (__bridge id)_curFrame.CGImage;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentsRect:(CGRect)rect forImage:(UIImage *)image{
|
||||
CGRect layerRect = CGRectMake(0, 0, 1, 1);
|
||||
if (image) {
|
||||
CGSize imageSize = image.size;
|
||||
if (imageSize.width > 0.01 && imageSize.height > 0.01) {
|
||||
layerRect.origin.x = rect.origin.x / imageSize.width;
|
||||
layerRect.origin.y = rect.origin.y / imageSize.height;
|
||||
layerRect.size.width = rect.size.width / imageSize.width;
|
||||
layerRect.size.height = rect.size.height / imageSize.height;
|
||||
layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1));
|
||||
if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) {
|
||||
layerRect = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
self.layer.contentsRect = layerRect;
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)didMoved {
|
||||
if (self.autoPlayAnimatedImage) {
|
||||
if(self.superview && self.window) {
|
||||
[self startAnimating];
|
||||
} else {
|
||||
[self stopAnimating];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow {
|
||||
[super didMoveToWindow];
|
||||
[self didMoved];
|
||||
}
|
||||
|
||||
- (void)didMoveToSuperview {
|
||||
[super didMoveToSuperview];
|
||||
[self didMoved];
|
||||
}
|
||||
|
||||
- (void)setCurrentAnimatedImageIndex:(NSUInteger)currentAnimatedImageIndex {
|
||||
if (!_curAnimatedImage) return;
|
||||
if (currentAnimatedImageIndex >= _curAnimatedImage.animatedImageFrameCount) return;
|
||||
if (_curIndex == currentAnimatedImageIndex) return;
|
||||
|
||||
void (^block)() = ^{
|
||||
LOCK(
|
||||
[_requestQueue cancelAllOperations];
|
||||
[_buffer removeAllObjects];
|
||||
[self willChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
_curIndex = currentAnimatedImageIndex;
|
||||
[self didChangeValueForKey:@"currentAnimatedImageIndex"];
|
||||
_curFrame = [_curAnimatedImage animatedImageFrameAtIndex:_curIndex];
|
||||
if (_curImageHasContentsRect) {
|
||||
_curContentsRect = [_curAnimatedImage animatedImageContentsRectAtIndex:_curIndex];
|
||||
}
|
||||
_time = 0;
|
||||
_loopEnd = NO;
|
||||
_bufferMiss = NO;
|
||||
[self.layer setNeedsDisplay];
|
||||
)//LOCK
|
||||
};
|
||||
|
||||
if (pthread_main_np()) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)currentAnimatedImageIndex {
|
||||
return _curIndex;
|
||||
}
|
||||
|
||||
- (void)setRunloopMode:(NSString *)runloopMode {
|
||||
if ([_runloopMode isEqual:runloopMode]) return;
|
||||
if (_link) {
|
||||
if (_runloopMode) {
|
||||
[_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
|
||||
}
|
||||
if (runloopMode.length) {
|
||||
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:runloopMode];
|
||||
}
|
||||
}
|
||||
_runloopMode = runloopMode.copy;
|
||||
}
|
||||
|
||||
#pragma mark - Override NSObject(NSKeyValueObservingCustomization)
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
|
||||
if ([key isEqualToString:@"currentAnimatedImageIndex"]) {
|
||||
return NO;
|
||||
}
|
||||
return [super automaticallyNotifiesObserversForKey:key];
|
||||
}
|
||||
|
||||
#pragma mark - NSCoding
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
_runloopMode = [aDecoder decodeObjectForKey:@"runloopMode"];
|
||||
if (_runloopMode.length == 0) _runloopMode = NSRunLoopCommonModes;
|
||||
if ([aDecoder containsValueForKey:@"autoPlayAnimatedImage"]) {
|
||||
_autoPlayAnimatedImage = [aDecoder decodeBoolForKey:@"autoPlayAnimatedImage"];
|
||||
} else {
|
||||
_autoPlayAnimatedImage = YES;
|
||||
}
|
||||
|
||||
UIImage *image = [aDecoder decodeObjectForKey:@"YYAnimatedImage"];
|
||||
UIImage *highlightedImage = [aDecoder decodeObjectForKey:@"YYHighlightedAnimatedImage"];
|
||||
if (image) {
|
||||
self.image = image;
|
||||
[self setImage:image withType:YYAnimatedImageTypeImage];
|
||||
}
|
||||
if (highlightedImage) {
|
||||
self.highlightedImage = highlightedImage;
|
||||
[self setImage:highlightedImage withType:YYAnimatedImageTypeHighlightedImage];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder:aCoder];
|
||||
[aCoder encodeObject:_runloopMode forKey:@"runloopMode"];
|
||||
[aCoder encodeBool:_autoPlayAnimatedImage forKey:@"autoPlayAnimatedImage"];
|
||||
|
||||
BOOL ani, multi;
|
||||
ani = [self.image conformsToProtocol:@protocol(YYAnimatedImage)];
|
||||
multi = (ani && ((UIImage <YYAnimatedImage> *)self.image).animatedImageFrameCount > 1);
|
||||
if (multi) [aCoder encodeObject:self.image forKey:@"YYAnimatedImage"];
|
||||
|
||||
ani = [self.highlightedImage conformsToProtocol:@protocol(YYAnimatedImage)];
|
||||
multi = (ani && ((UIImage <YYAnimatedImage> *)self.highlightedImage).animatedImageFrameCount > 1);
|
||||
if (multi) [aCoder encodeObject:self.highlightedImage forKey:@"YYHighlightedAnimatedImage"];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// YYFrameImage.h
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/12/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
#import <YYImage/YYAnimatedImageView.h>
|
||||
#elif __has_include(<YYWebImage/YYImage.h>)
|
||||
#import <YYWebImage/YYAnimatedImageView.h>
|
||||
#else
|
||||
#import "YYAnimatedImageView.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An image to display frame-based animation.
|
||||
|
||||
@discussion It is a fully compatible `UIImage` subclass.
|
||||
It only support system image format such as png and jpeg.
|
||||
The animation can be played by YYAnimatedImageView.
|
||||
|
||||
Sample Code:
|
||||
|
||||
NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"];
|
||||
NSArray *times = @[@0.1, @0.2, @0.1];
|
||||
YYFrameImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES];
|
||||
YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
|
||||
[view addSubView:imageView];
|
||||
*/
|
||||
@interface YYFrameImage : UIImage <YYAnimatedImage>
|
||||
|
||||
/**
|
||||
Create a frame animated image from files.
|
||||
|
||||
@param paths An array of NSString objects, contains the full or
|
||||
partial path to each image file.
|
||||
e.g. @[@"/ani/1.png",@"/ani/2.png",@"/ani/3.png"]
|
||||
|
||||
@param oneFrameDuration The duration (in seconds) per frame.
|
||||
|
||||
@param loopCount The animation loop count, 0 means infinite.
|
||||
|
||||
@return An initialized YYFrameImage object, or nil when an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithImagePaths:(NSArray<NSString *> *)paths
|
||||
oneFrameDuration:(NSTimeInterval)oneFrameDuration
|
||||
loopCount:(NSUInteger)loopCount;
|
||||
|
||||
/**
|
||||
Create a frame animated image from files.
|
||||
|
||||
@param paths An array of NSString objects, contains the full or
|
||||
partial path to each image file.
|
||||
e.g. @[@"/ani/frame1.png",@"/ani/frame2.png",@"/ani/frame3.png"]
|
||||
|
||||
@param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame.
|
||||
e.g. @[@0.1, @0.2, @0.3];
|
||||
|
||||
@param loopCount The animation loop count, 0 means infinite.
|
||||
|
||||
@return An initialized YYFrameImage object, or nil when an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithImagePaths:(NSArray<NSString *> *)paths
|
||||
frameDurations:(NSArray<NSNumber *> *)frameDurations
|
||||
loopCount:(NSUInteger)loopCount;
|
||||
|
||||
/**
|
||||
Create a frame animated image from an array of data.
|
||||
|
||||
@param dataArray An array of NSData objects.
|
||||
|
||||
@param oneFrameDuration The duration (in seconds) per frame.
|
||||
|
||||
@param loopCount The animation loop count, 0 means infinite.
|
||||
|
||||
@return An initialized YYFrameImage object, or nil when an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithImageDataArray:(NSArray<NSData *> *)dataArray
|
||||
oneFrameDuration:(NSTimeInterval)oneFrameDuration
|
||||
loopCount:(NSUInteger)loopCount;
|
||||
|
||||
/**
|
||||
Create a frame animated image from an array of data.
|
||||
|
||||
@param dataArray An array of NSData objects.
|
||||
|
||||
@param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame.
|
||||
e.g. @[@0.1, @0.2, @0.3];
|
||||
|
||||
@param loopCount The animation loop count, 0 means infinite.
|
||||
|
||||
@return An initialized YYFrameImage object, or nil when an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithImageDataArray:(NSArray<NSData *> *)dataArray
|
||||
frameDurations:(NSArray *)frameDurations
|
||||
loopCount:(NSUInteger)loopCount;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// YYFrameImage.m
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/12/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYFrameImage.h"
|
||||
#import "YYImageCoder.h"
|
||||
|
||||
|
||||
/**
|
||||
Return the path scale.
|
||||
|
||||
e.g.
|
||||
<table>
|
||||
<tr><th>Path </th><th>Scale </th></tr>
|
||||
<tr><td>"icon.png" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x.png" </td><td>2 </td></tr>
|
||||
<tr><td>"icon@2.5x.png" </td><td>2.5 </td></tr>
|
||||
<tr><td>"icon@2x" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x..png" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x.png/" </td><td>1 </td></tr>
|
||||
</table>
|
||||
*/
|
||||
static CGFloat _NSStringPathScale(NSString *string) {
|
||||
if (string.length == 0 || [string hasSuffix:@"/"]) return 1;
|
||||
NSString *name = string.stringByDeletingPathExtension;
|
||||
__block CGFloat scale = 1;
|
||||
|
||||
NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines error:nil];
|
||||
[pattern enumerateMatchesInString:name options:kNilOptions range:NSMakeRange(0, name.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
if (result.range.location >= 3) {
|
||||
scale = [string substringWithRange:NSMakeRange(result.range.location + 1, result.range.length - 2)].doubleValue;
|
||||
}
|
||||
}];
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@implementation YYFrameImage {
|
||||
NSUInteger _loopCount;
|
||||
NSUInteger _oneFrameBytes;
|
||||
NSArray *_imagePaths;
|
||||
NSArray *_imageDatas;
|
||||
NSArray *_frameDurations;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImagePaths:(NSArray *)paths oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount {
|
||||
NSMutableArray *durations = [NSMutableArray new];
|
||||
for (int i = 0, max = (int)paths.count; i < max; i++) {
|
||||
[durations addObject:@(oneFrameDuration)];
|
||||
}
|
||||
return [self initWithImagePaths:paths frameDurations:durations loopCount:loopCount];
|
||||
}
|
||||
|
||||
- (instancetype)initWithImagePaths:(NSArray *)paths frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount {
|
||||
if (paths.count == 0) return nil;
|
||||
if (paths.count != frameDurations.count) return nil;
|
||||
|
||||
NSString *firstPath = paths[0];
|
||||
NSData *firstData = [NSData dataWithContentsOfFile:firstPath];
|
||||
CGFloat scale = _NSStringPathScale(firstPath);
|
||||
UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] yy_imageByDecoded];
|
||||
self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp];
|
||||
if (!self) return nil;
|
||||
long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage);
|
||||
_oneFrameBytes = (NSUInteger)frameByte;
|
||||
_imagePaths = paths.copy;
|
||||
_frameDurations = frameDurations.copy;
|
||||
_loopCount = loopCount;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImageDataArray:(NSArray *)dataArray oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount {
|
||||
NSMutableArray *durations = [NSMutableArray new];
|
||||
for (int i = 0, max = (int)dataArray.count; i < max; i++) {
|
||||
[durations addObject:@(oneFrameDuration)];
|
||||
}
|
||||
return [self initWithImageDataArray:dataArray frameDurations:durations loopCount:loopCount];
|
||||
}
|
||||
|
||||
- (instancetype)initWithImageDataArray:(NSArray *)dataArray frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount {
|
||||
if (dataArray.count == 0) return nil;
|
||||
if (dataArray.count != frameDurations.count) return nil;
|
||||
|
||||
NSData *firstData = dataArray[0];
|
||||
CGFloat scale = [UIScreen mainScreen].scale;
|
||||
UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] yy_imageByDecoded];
|
||||
self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp];
|
||||
if (!self) return nil;
|
||||
long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage);
|
||||
_oneFrameBytes = (NSUInteger)frameByte;
|
||||
_imageDatas = dataArray.copy;
|
||||
_frameDurations = frameDurations.copy;
|
||||
_loopCount = loopCount;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - YYAnimtedImage
|
||||
|
||||
- (NSUInteger)animatedImageFrameCount {
|
||||
if (_imagePaths) {
|
||||
return _imagePaths.count;
|
||||
} else if (_imageDatas) {
|
||||
return _imageDatas.count;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageLoopCount {
|
||||
return _loopCount;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageBytesPerFrame {
|
||||
return _oneFrameBytes;
|
||||
}
|
||||
|
||||
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
|
||||
if (_imagePaths) {
|
||||
if (index >= _imagePaths.count) return nil;
|
||||
NSString *path = _imagePaths[index];
|
||||
CGFloat scale = _NSStringPathScale(path);
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
return [[UIImage imageWithData:data scale:scale] yy_imageByDecoded];
|
||||
} else if (_imageDatas) {
|
||||
if (index >= _imageDatas.count) return nil;
|
||||
NSData *data = _imageDatas[index];
|
||||
return [[UIImage imageWithData:data scale:[UIScreen mainScreen].scale] yy_imageByDecoded];
|
||||
} else {
|
||||
return index == 0 ? self : nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
|
||||
if (index >= _frameDurations.count) return 0;
|
||||
NSNumber *num = _frameDurations[index];
|
||||
return [num doubleValue];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// YYImage.h
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/10/20.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
FOUNDATION_EXPORT double YYImageVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char YYImageVersionString[];
|
||||
#import <YYImage/YYFrameImage.h>
|
||||
#import <YYImage/YYSpriteSheetImage.h>
|
||||
#import <YYImage/YYImageCoder.h>
|
||||
#import <YYImage/YYAnimatedImageView.h>
|
||||
#elif __has_include(<YYWebImage/YYImage.h>)
|
||||
#import <YYWebImage/YYFrameImage.h>
|
||||
#import <YYWebImage/YYSpriteSheetImage.h>
|
||||
#import <YYWebImage/YYImageCoder.h>
|
||||
#import <YYWebImage/YYAnimatedImageView.h>
|
||||
#else
|
||||
#import "YYFrameImage.h"
|
||||
#import "YYSpriteSheetImage.h"
|
||||
#import "YYImageCoder.h"
|
||||
#import "YYAnimatedImageView.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
A YYImage object is a high-level way to display animated image data.
|
||||
|
||||
@discussion It is a fully compatible `UIImage` subclass. It extends the UIImage
|
||||
to support animated WebP, APNG and GIF format image data decoding. It also
|
||||
support NSCoding protocol to archive and unarchive multi-frame image data.
|
||||
|
||||
If the image is created from multi-frame image data, and you want to play the
|
||||
animation, try replace UIImageView with `YYAnimatedImageView`.
|
||||
|
||||
Sample Code:
|
||||
|
||||
// animation@3x.webp
|
||||
YYImage *image = [YYImage imageNamed:@"animation.webp"];
|
||||
YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
|
||||
[view addSubView:imageView];
|
||||
|
||||
*/
|
||||
@interface YYImage : UIImage <YYAnimatedImage>
|
||||
|
||||
+ (nullable YYImage *)imageNamed:(NSString *)name; // no cache!
|
||||
+ (nullable YYImage *)imageWithContentsOfFile:(NSString *)path;
|
||||
+ (nullable YYImage *)imageWithData:(NSData *)data;
|
||||
+ (nullable YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale;
|
||||
|
||||
/**
|
||||
If the image is created from data or file, then the value indicates the data type.
|
||||
*/
|
||||
@property (nonatomic, readonly) YYImageType animatedImageType;
|
||||
|
||||
/**
|
||||
If the image is created from animated image data (multi-frame GIF/APNG/WebP),
|
||||
this property stores the original image data.
|
||||
*/
|
||||
@property (nullable, nonatomic, readonly) NSData *animatedImageData;
|
||||
|
||||
/**
|
||||
The total memory usage (in bytes) if all frame images was loaded into memory.
|
||||
The value is 0 if the image is not created from a multi-frame image data.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSUInteger animatedImageMemorySize;
|
||||
|
||||
/**
|
||||
Preload all frame image to memory.
|
||||
|
||||
@discussion Set this property to `YES` will block the calling thread to decode
|
||||
all animation frame image to memory, set to `NO` will release the preloaded frames.
|
||||
If the image is shared by lots of image views (such as emoticon), preload all
|
||||
frames will reduce the CPU cost.
|
||||
|
||||
See `animatedImageMemorySize` for memory cost.
|
||||
*/
|
||||
@property (nonatomic) BOOL preloadAllAnimatedImageFrames;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,254 @@
|
|||
//
|
||||
// YYImage.m
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 14/10/20.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYImage.h"
|
||||
|
||||
/**
|
||||
An array of NSNumber objects, shows the best order for path scale search.
|
||||
e.g. iPhone3GS:@[@1,@2,@3] iPhone5:@[@2,@3,@1] iPhone6 Plus:@[@3,@2,@1]
|
||||
*/
|
||||
static NSArray *_NSBundlePreferredScales() {
|
||||
static NSArray *scales;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||
if (screenScale <= 1) {
|
||||
scales = @[@1,@2,@3];
|
||||
} else if (screenScale <= 2) {
|
||||
scales = @[@2,@3,@1];
|
||||
} else {
|
||||
scales = @[@3,@2,@1];
|
||||
}
|
||||
});
|
||||
return scales;
|
||||
}
|
||||
|
||||
/**
|
||||
Add scale modifier to the file name (without path extension),
|
||||
From @"name" to @"name@2x".
|
||||
|
||||
e.g.
|
||||
<table>
|
||||
<tr><th>Before </th><th>After(scale:2)</th></tr>
|
||||
<tr><td>"icon" </td><td>"icon@2x" </td></tr>
|
||||
<tr><td>"icon " </td><td>"icon @2x" </td></tr>
|
||||
<tr><td>"icon.top" </td><td>"icon.top@2x" </td></tr>
|
||||
<tr><td>"/p/name" </td><td>"/p/name@2x" </td></tr>
|
||||
<tr><td>"/path/" </td><td>"/path/" </td></tr>
|
||||
</table>
|
||||
|
||||
@param scale Resource scale.
|
||||
@return String by add scale modifier, or just return if it's not end with file name.
|
||||
*/
|
||||
static NSString *_NSStringByAppendingNameScale(NSString *string, CGFloat scale) {
|
||||
if (!string) return nil;
|
||||
if (fabs(scale - 1) <= __FLT_EPSILON__ || string.length == 0 || [string hasSuffix:@"/"]) return string.copy;
|
||||
return [string stringByAppendingFormat:@"@%@x", @(scale)];
|
||||
}
|
||||
|
||||
/**
|
||||
Return the path scale.
|
||||
|
||||
e.g.
|
||||
<table>
|
||||
<tr><th>Path </th><th>Scale </th></tr>
|
||||
<tr><td>"icon.png" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x.png" </td><td>2 </td></tr>
|
||||
<tr><td>"icon@2.5x.png" </td><td>2.5 </td></tr>
|
||||
<tr><td>"icon@2x" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x..png" </td><td>1 </td></tr>
|
||||
<tr><td>"icon@2x.png/" </td><td>1 </td></tr>
|
||||
</table>
|
||||
*/
|
||||
static CGFloat _NSStringPathScale(NSString *string) {
|
||||
if (string.length == 0 || [string hasSuffix:@"/"]) return 1;
|
||||
NSString *name = string.stringByDeletingPathExtension;
|
||||
__block CGFloat scale = 1;
|
||||
|
||||
NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:@"@[0-9]+\\.?[0-9]*x$" options:NSRegularExpressionAnchorsMatchLines error:nil];
|
||||
[pattern enumerateMatchesInString:name options:kNilOptions range:NSMakeRange(0, name.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
if (result.range.location >= 3) {
|
||||
scale = [string substringWithRange:NSMakeRange(result.range.location + 1, result.range.length - 2)].doubleValue;
|
||||
}
|
||||
}];
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
|
||||
@implementation YYImage {
|
||||
YYImageDecoder *_decoder;
|
||||
NSArray *_preloadedFrames;
|
||||
dispatch_semaphore_t _preloadedLock;
|
||||
NSUInteger _bytesPerFrame;
|
||||
}
|
||||
|
||||
+ (YYImage *)imageNamed:(NSString *)name {
|
||||
if (name.length == 0) return nil;
|
||||
if ([name hasSuffix:@"/"]) return nil;
|
||||
|
||||
NSString *res = name.stringByDeletingPathExtension;
|
||||
NSString *ext = name.pathExtension;
|
||||
NSString *path = nil;
|
||||
CGFloat scale = 1;
|
||||
|
||||
// If no extension, guess by system supported (same as UIImage).
|
||||
NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"];
|
||||
NSArray *scales = _NSBundlePreferredScales();
|
||||
for (int s = 0; s < scales.count; s++) {
|
||||
scale = ((NSNumber *)scales[s]).floatValue;
|
||||
NSString *scaledName = _NSStringByAppendingNameScale(res, scale);
|
||||
for (NSString *e in exts) {
|
||||
path = [[NSBundle mainBundle] pathForResource:scaledName ofType:e];
|
||||
if (path) break;
|
||||
}
|
||||
if (path) break;
|
||||
}
|
||||
if (path.length == 0) return nil;
|
||||
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
if (data.length == 0) return nil;
|
||||
|
||||
return [[self alloc] initWithData:data scale:scale];
|
||||
}
|
||||
|
||||
+ (YYImage *)imageWithContentsOfFile:(NSString *)path {
|
||||
return [[self alloc] initWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
+ (YYImage *)imageWithData:(NSData *)data {
|
||||
return [[self alloc] initWithData:data];
|
||||
}
|
||||
|
||||
+ (YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale {
|
||||
return [[self alloc] initWithData:data scale:scale];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentsOfFile:(NSString *)path {
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
return [self initWithData:data scale:_NSStringPathScale(path)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data {
|
||||
return [self initWithData:data scale:1];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale {
|
||||
if (data.length == 0) return nil;
|
||||
if (scale <= 0) scale = [UIScreen mainScreen].scale;
|
||||
_preloadedLock = dispatch_semaphore_create(1);
|
||||
@autoreleasepool {
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];
|
||||
YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:YES];
|
||||
UIImage *image = frame.image;
|
||||
if (!image) return nil;
|
||||
self = [self initWithCGImage:image.CGImage scale:decoder.scale orientation:image.imageOrientation];
|
||||
if (!self) return nil;
|
||||
_animatedImageType = decoder.type;
|
||||
if (decoder.frameCount > 1) {
|
||||
_decoder = decoder;
|
||||
_bytesPerFrame = CGImageGetBytesPerRow(image.CGImage) * CGImageGetHeight(image.CGImage);
|
||||
_animatedImageMemorySize = _bytesPerFrame * decoder.frameCount;
|
||||
}
|
||||
self.yy_isDecodedForDisplay = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)animatedImageData {
|
||||
return _decoder.data;
|
||||
}
|
||||
|
||||
- (void)setPreloadAllAnimatedImageFrames:(BOOL)preloadAllAnimatedImageFrames {
|
||||
if (_preloadAllAnimatedImageFrames != preloadAllAnimatedImageFrames) {
|
||||
if (preloadAllAnimatedImageFrames && _decoder.frameCount > 0) {
|
||||
NSMutableArray *frames = [NSMutableArray new];
|
||||
for (NSUInteger i = 0, max = _decoder.frameCount; i < max; i++) {
|
||||
UIImage *img = [self animatedImageFrameAtIndex:i];
|
||||
if (img) {
|
||||
[frames addObject:img];
|
||||
} else {
|
||||
[frames addObject:[NSNull null]];
|
||||
}
|
||||
}
|
||||
dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
|
||||
_preloadedFrames = frames;
|
||||
dispatch_semaphore_signal(_preloadedLock);
|
||||
} else {
|
||||
dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
|
||||
_preloadedFrames = nil;
|
||||
dispatch_semaphore_signal(_preloadedLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - protocol NSCoding
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
NSNumber *scale = [aDecoder decodeObjectForKey:@"YYImageScale"];
|
||||
NSData *data = [aDecoder decodeObjectForKey:@"YYImageData"];
|
||||
if (data.length) {
|
||||
self = [self initWithData:data scale:scale.doubleValue];
|
||||
} else {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
if (_decoder.data.length) {
|
||||
[aCoder encodeObject:@(self.scale) forKey:@"YYImageScale"];
|
||||
[aCoder encodeObject:_decoder.data forKey:@"YYImageData"];
|
||||
} else {
|
||||
[super encodeWithCoder:aCoder]; // Apple use UIImagePNGRepresentation() to encode UIImage.
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - protocol YYAnimatedImage
|
||||
|
||||
- (NSUInteger)animatedImageFrameCount {
|
||||
return _decoder.frameCount;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageLoopCount {
|
||||
return _decoder.loopCount;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageBytesPerFrame {
|
||||
return _bytesPerFrame;
|
||||
}
|
||||
|
||||
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
|
||||
if (index >= _decoder.frameCount) return nil;
|
||||
dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
|
||||
UIImage *image = _preloadedFrames[index];
|
||||
dispatch_semaphore_signal(_preloadedLock);
|
||||
if (image) return image == (id)[NSNull null] ? nil : image;
|
||||
return [_decoder frameAtIndex:index decodeForDisplay:YES].image;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
|
||||
NSTimeInterval duration = [_decoder frameDurationAtIndex:index];
|
||||
|
||||
/*
|
||||
http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
|
||||
Many annoying ads specify a 0 duration to make an image flash as quickly as
|
||||
possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
|
||||
for any frames that specify a duration of <= 10 ms.
|
||||
See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.
|
||||
|
||||
See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
|
||||
*/
|
||||
if (duration < 0.011f) return 0.100f;
|
||||
return duration;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,505 @@
|
|||
//
|
||||
// YYImageCoder.h
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 15/5/13.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Image file type.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, YYImageType) {
|
||||
YYImageTypeUnknown = 0, ///< unknown
|
||||
YYImageTypeJPEG, ///< jpeg, jpg
|
||||
YYImageTypeJPEG2000, ///< jp2
|
||||
YYImageTypeTIFF, ///< tiff, tif
|
||||
YYImageTypeBMP, ///< bmp
|
||||
YYImageTypeICO, ///< ico
|
||||
YYImageTypeICNS, ///< icns
|
||||
YYImageTypeGIF, ///< gif
|
||||
YYImageTypePNG, ///< png
|
||||
YYImageTypeWebP, ///< webp
|
||||
YYImageTypeOther, ///< other image format
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Dispose method specifies how the area used by the current frame is to be treated
|
||||
before rendering the next frame on the canvas.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, YYImageDisposeMethod) {
|
||||
|
||||
/**
|
||||
No disposal is done on this frame before rendering the next; the contents
|
||||
of the canvas are left as is.
|
||||
*/
|
||||
YYImageDisposeNone = 0,
|
||||
|
||||
/**
|
||||
The frame's region of the canvas is to be cleared to fully transparent black
|
||||
before rendering the next frame.
|
||||
*/
|
||||
YYImageDisposeBackground,
|
||||
|
||||
/**
|
||||
The frame's region of the canvas is to be reverted to the previous contents
|
||||
before rendering the next frame.
|
||||
*/
|
||||
YYImageDisposePrevious,
|
||||
};
|
||||
|
||||
/**
|
||||
Blend operation specifies how transparent pixels of the current frame are
|
||||
blended with those of the previous canvas.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, YYImageBlendOperation) {
|
||||
|
||||
/**
|
||||
All color components of the frame, including alpha, overwrite the current
|
||||
contents of the frame's canvas region.
|
||||
*/
|
||||
YYImageBlendNone = 0,
|
||||
|
||||
/**
|
||||
The frame should be composited onto the output buffer based on its alpha.
|
||||
*/
|
||||
YYImageBlendOver,
|
||||
};
|
||||
|
||||
/**
|
||||
An image frame object.
|
||||
*/
|
||||
@interface YYImageFrame : NSObject <NSCopying>
|
||||
@property (nonatomic) NSUInteger index; ///< Frame index (zero based)
|
||||
@property (nonatomic) NSUInteger width; ///< Frame width
|
||||
@property (nonatomic) NSUInteger height; ///< Frame height
|
||||
@property (nonatomic) NSUInteger offsetX; ///< Frame origin.x in canvas (left-bottom based)
|
||||
@property (nonatomic) NSUInteger offsetY; ///< Frame origin.y in canvas (left-bottom based)
|
||||
@property (nonatomic) NSTimeInterval duration; ///< Frame duration in seconds
|
||||
@property (nonatomic) YYImageDisposeMethod dispose; ///< Frame dispose method.
|
||||
@property (nonatomic) YYImageBlendOperation blend; ///< Frame blend operation.
|
||||
@property (nullable, nonatomic, strong) UIImage *image; ///< The image.
|
||||
+ (instancetype)frameWithImage:(UIImage *)image;
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - Decoder
|
||||
|
||||
/**
|
||||
An image decoder to decode image data.
|
||||
|
||||
@discussion This class supports decoding animated WebP, APNG, GIF and system
|
||||
image format such as PNG, JPG, JP2, BMP, TIFF, PIC, ICNS and ICO. It can be used
|
||||
to decode complete image data, or to decode incremental image data during image
|
||||
download. This class is thread-safe.
|
||||
|
||||
Example:
|
||||
|
||||
// Decode single image:
|
||||
NSData *data = [NSData dataWithContentOfFile:@"/tmp/image.webp"];
|
||||
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
|
||||
// Decode image during download:
|
||||
NSMutableData *data = [NSMutableData new];
|
||||
YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0];
|
||||
while(newDataArrived) {
|
||||
[data appendData:newData];
|
||||
[decoder updateData:data final:NO];
|
||||
if (decoder.frameCount > 0) {
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// progressive display...
|
||||
}
|
||||
}
|
||||
[decoder updateData:data final:YES];
|
||||
UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
|
||||
// final display...
|
||||
|
||||
*/
|
||||
@interface YYImageDecoder : NSObject
|
||||
|
||||
@property (nullable, nonatomic, readonly) NSData *data; ///< Image data.
|
||||
@property (nonatomic, readonly) YYImageType type; ///< Image data type.
|
||||
@property (nonatomic, readonly) CGFloat scale; ///< Image scale.
|
||||
@property (nonatomic, readonly) NSUInteger frameCount; ///< Image frame count.
|
||||
@property (nonatomic, readonly) NSUInteger loopCount; ///< Image loop count, 0 means infinite.
|
||||
@property (nonatomic, readonly) NSUInteger width; ///< Image canvas width.
|
||||
@property (nonatomic, readonly) NSUInteger height; ///< Image canvas height.
|
||||
@property (nonatomic, readonly, getter=isFinalized) BOOL finalized;
|
||||
|
||||
/**
|
||||
Creates an image decoder.
|
||||
|
||||
@param scale Image's scale.
|
||||
@return An image decoder.
|
||||
*/
|
||||
- (instancetype)initWithScale:(CGFloat)scale NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Updates the incremental image with new data.
|
||||
|
||||
@discussion You can use this method to decode progressive/interlaced/baseline
|
||||
image when you do not have the complete image data. The `data` was retained by
|
||||
decoder, you should not modify the data in other thread during decoding.
|
||||
|
||||
@param data The data to add to the image decoder. Each time you call this
|
||||
function, the 'data' parameter must contain all of the image file data
|
||||
accumulated so far.
|
||||
|
||||
@param final A value that specifies whether the data is the final set.
|
||||
Pass YES if it is, NO otherwise. When the data is already finalized, you can
|
||||
not update the data anymore.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)updateData:(nullable NSData *)data final:(BOOL)final;
|
||||
|
||||
/**
|
||||
Convenience method to create a decoder with specified data.
|
||||
@param data Image data.
|
||||
@param scale Image's scale.
|
||||
@return A new decoder, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)decoderWithData:(NSData *)data scale:(CGFloat)scale;
|
||||
|
||||
/**
|
||||
Decodes and returns a frame from a specified index.
|
||||
@param index Frame image index (zero-based).
|
||||
@param decodeForDisplay Whether decode the image to memory bitmap for display.
|
||||
If NO, it will try to returns the original frame data without blend.
|
||||
@return A new frame with image, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable YYImageFrame *)frameAtIndex:(NSUInteger)index decodeForDisplay:(BOOL)decodeForDisplay;
|
||||
|
||||
/**
|
||||
Returns the frame duration from a specified index.
|
||||
@param index Frame image (zero-based).
|
||||
@return Duration in seconds.
|
||||
*/
|
||||
- (NSTimeInterval)frameDurationAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Returns the frame's properties. See "CGImageProperties.h" in ImageIO.framework
|
||||
for more information.
|
||||
|
||||
@param index Frame image index (zero-based).
|
||||
@return The ImageIO frame property.
|
||||
*/
|
||||
- (nullable NSDictionary *)framePropertiesAtIndex:(NSUInteger)index;
|
||||
|
||||
/**
|
||||
Returns the image's properties. See "CGImageProperties.h" in ImageIO.framework
|
||||
for more information.
|
||||
*/
|
||||
- (nullable NSDictionary *)imageProperties;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#pragma mark - Encoder
|
||||
|
||||
/**
|
||||
An image encoder to encode image to data.
|
||||
|
||||
@discussion It supports encoding single frame image with the type defined in YYImageType.
|
||||
It also supports encoding multi-frame image with GIF, APNG and WebP.
|
||||
|
||||
Example:
|
||||
|
||||
YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG];
|
||||
jpegEncoder.quality = 0.9;
|
||||
[jpegEncoder addImage:image duration:0];
|
||||
NSData jpegData = [jpegEncoder encode];
|
||||
|
||||
YYImageEncoder *gifEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeGIF];
|
||||
gifEncoder.loopCount = 5;
|
||||
[gifEncoder addImage:image0 duration:0.1];
|
||||
[gifEncoder addImage:image1 duration:0.15];
|
||||
[gifEncoder addImage:image2 duration:0.2];
|
||||
NSData gifData = [gifEncoder encode];
|
||||
|
||||
@warning It just pack the images together when encoding multi-frame image. If you
|
||||
want to reduce the image file size, try imagemagick/ffmpeg for GIF and WebP,
|
||||
and apngasm for APNG.
|
||||
*/
|
||||
@interface YYImageEncoder : NSObject
|
||||
|
||||
@property (nonatomic, readonly) YYImageType type; ///< Image type.
|
||||
@property (nonatomic) NSUInteger loopCount; ///< Loop count, 0 means infinit, only available for GIF/APNG/WebP.
|
||||
@property (nonatomic) BOOL lossless; ///< Lossless, only available for WebP.
|
||||
@property (nonatomic) CGFloat quality; ///< Compress quality, 0.0~1.0, only available for JPG/JP2/WebP.
|
||||
|
||||
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
Create an image encoder with a specified type.
|
||||
@param type Image type.
|
||||
@return A new encoder, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithType:(YYImageType)type NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Add an image to encoder.
|
||||
@param image Image.
|
||||
@param duration Image duration for animation. Pass 0 to ignore this parameter.
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image duration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Add an image with image data to encoder.
|
||||
@param data Image data.
|
||||
@param duration Image duration for animation. Pass 0 to ignore this parameter.
|
||||
*/
|
||||
- (void)addImageWithData:(NSData *)data duration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Add an image from a file path to encoder.
|
||||
@param image Image file path.
|
||||
@param duration Image duration for animation. Pass 0 to ignore this parameter.
|
||||
*/
|
||||
- (void)addImageWithFile:(NSString *)path duration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Encodes the image and returns the image data.
|
||||
@return The image data, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable NSData *)encode;
|
||||
|
||||
/**
|
||||
Encodes the image to a file.
|
||||
@param path The file path (overwrite if exist).
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)encodeToFile:(NSString *)path;
|
||||
|
||||
/**
|
||||
Convenience method to encode single frame image.
|
||||
@param image The image.
|
||||
@param type The destination image type.
|
||||
@param quality Image quality, 0.0~1.0.
|
||||
@return The image data, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSData *)encodeImage:(UIImage *)image type:(YYImageType)type quality:(CGFloat)quality;
|
||||
|
||||
/**
|
||||
Convenience method to encode image from a decoder.
|
||||
@param decoder The image decoder.
|
||||
@param type The destination image type;
|
||||
@param quality Image quality, 0.0~1.0.
|
||||
@return The image data, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSData *)encodeImageWithDecoder:(YYImageDecoder *)decoder type:(YYImageType)type quality:(CGFloat)quality;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - UIImage
|
||||
|
||||
@interface UIImage (YYImageCoder)
|
||||
|
||||
/**
|
||||
Decompress this image to bitmap, so when the image is displayed on screen,
|
||||
the main thread won't be blocked by additional decode. If the image has already
|
||||
been decoded or unable to decode, it just returns itself.
|
||||
|
||||
@return an image decoded, or just return itself if no needed.
|
||||
@see yy_isDecodedForDisplay
|
||||
*/
|
||||
- (instancetype)yy_imageByDecoded;
|
||||
|
||||
/**
|
||||
Wherher the image can be display on screen without additional decoding.
|
||||
@warning It just a hint for your code, change it has no other effect.
|
||||
*/
|
||||
@property (nonatomic) BOOL yy_isDecodedForDisplay;
|
||||
|
||||
/**
|
||||
Saves this image to iOS Photos Album.
|
||||
|
||||
@discussion This method attempts to save the original data to album if the
|
||||
image is created from an animated GIF/APNG, otherwise, it will save the image
|
||||
as JPEG or PNG (based on the alpha information).
|
||||
|
||||
@param completionBlock The block invoked (in main thread) after the save operation completes.
|
||||
assetURL: An URL that identifies the saved image file. If the image is not saved, assetURL is nil.
|
||||
error: If the image is not saved, an error object that describes the reason for failure, otherwise nil.
|
||||
*/
|
||||
- (void)yy_saveToAlbumWithCompletionBlock:(nullable void(^)(NSURL * _Nullable assetURL, NSError * _Nullable error))completionBlock;
|
||||
|
||||
/**
|
||||
Return a 'best' data representation for this image.
|
||||
|
||||
@discussion The convertion based on these rule:
|
||||
1. If the image is created from an animated GIF/APNG/WebP, it returns the original data.
|
||||
2. It returns PNG or JPEG(0.9) representation based on the alpha information.
|
||||
|
||||
@return Image data, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable NSData *)yy_imageDataRepresentation;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
/// Detect a data's image type by reading the data's header 16 bytes (very fast).
|
||||
CG_EXTERN YYImageType YYImageDetectType(CFDataRef data);
|
||||
|
||||
/// Convert YYImageType to UTI (such as kUTTypeJPEG).
|
||||
CG_EXTERN CFStringRef _Nullable YYImageTypeToUTType(YYImageType type);
|
||||
|
||||
/// Convert UTI (such as kUTTypeJPEG) to YYImageType.
|
||||
CG_EXTERN YYImageType YYImageTypeFromUTType(CFStringRef uti);
|
||||
|
||||
/// Get image type's file extension (such as @"jpg").
|
||||
CG_EXTERN NSString *_Nullable YYImageTypeGetExtension(YYImageType type);
|
||||
|
||||
|
||||
|
||||
/// Returns the shared DeviceRGB color space.
|
||||
CG_EXTERN CGColorSpaceRef YYCGColorSpaceGetDeviceRGB();
|
||||
|
||||
/// Returns the shared DeviceGray color space.
|
||||
CG_EXTERN CGColorSpaceRef YYCGColorSpaceGetDeviceGray();
|
||||
|
||||
/// Returns whether a color space is DeviceRGB.
|
||||
CG_EXTERN BOOL YYCGColorSpaceIsDeviceRGB(CGColorSpaceRef space);
|
||||
|
||||
/// Returns whether a color space is DeviceGray.
|
||||
CG_EXTERN BOOL YYCGColorSpaceIsDeviceGray(CGColorSpaceRef space);
|
||||
|
||||
|
||||
|
||||
/// Convert EXIF orientation value to UIImageOrientation.
|
||||
CG_EXTERN UIImageOrientation YYUIImageOrientationFromEXIFValue(NSInteger value);
|
||||
|
||||
/// Convert UIImageOrientation to EXIF orientation value.
|
||||
CG_EXTERN NSInteger YYUIImageOrientationToEXIFValue(UIImageOrientation orientation);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Create a decoded image.
|
||||
|
||||
@discussion If the source image is created from a compressed image data (such as
|
||||
PNG or JPEG), you can use this method to decode the image. After decoded, you can
|
||||
access the decoded bytes with CGImageGetDataProvider() and CGDataProviderCopyData()
|
||||
without additional decode process. If the image has already decoded, this method
|
||||
just copy the decoded bytes to the new image.
|
||||
|
||||
@param imageRef The source image.
|
||||
@param decodeForDisplay If YES, this method will decode the image and convert
|
||||
it to BGRA8888 (premultiplied) or BGRX8888 format for CALayer display.
|
||||
|
||||
@return A decoded image, or NULL if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CGImageRef _Nullable YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay);
|
||||
|
||||
/**
|
||||
Create an image copy with an orientation.
|
||||
|
||||
@param imageRef Source image
|
||||
@param orientation Image orientation which will applied to the image.
|
||||
@param destBitmapInfo Destimation image bitmap, only support 32bit format (such as ARGB8888).
|
||||
@return A new image, or NULL if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CGImageRef _Nullable YYCGImageCreateCopyWithOrientation(CGImageRef imageRef,
|
||||
UIImageOrientation orientation,
|
||||
CGBitmapInfo destBitmapInfo);
|
||||
|
||||
/**
|
||||
Create an image copy with CGAffineTransform.
|
||||
|
||||
@param imageRef Source image.
|
||||
@param transform Transform applied to image (left-bottom based coordinate system).
|
||||
@param destSize Destination image size
|
||||
@param destBitmapInfo Destimation image bitmap, only support 32bit format (such as ARGB8888).
|
||||
@return A new image, or NULL if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CGImageRef _Nullable YYCGImageCreateAffineTransformCopy(CGImageRef imageRef,
|
||||
CGAffineTransform transform,
|
||||
CGSize destSize,
|
||||
CGBitmapInfo destBitmapInfo);
|
||||
|
||||
/**
|
||||
Encode an image to data with CGImageDestination.
|
||||
|
||||
@param imageRef The image.
|
||||
@param type The image destination data type.
|
||||
@param quality The quality (0.0~1.0)
|
||||
@return A new image data, or nil if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CFDataRef _Nullable YYCGImageCreateEncodedData(CGImageRef imageRef, YYImageType type, CGFloat quality);
|
||||
|
||||
|
||||
/**
|
||||
Whether WebP is available in YYImage.
|
||||
*/
|
||||
CG_EXTERN BOOL YYImageWebPAvailable();
|
||||
|
||||
/**
|
||||
Get a webp image frame count;
|
||||
|
||||
@param webpData WebP data.
|
||||
@return Image frame count, or 0 if an error occurs.
|
||||
*/
|
||||
CG_EXTERN NSUInteger YYImageGetWebPFrameCount(CFDataRef webpData);
|
||||
|
||||
/**
|
||||
Decode an image from WebP data, returns NULL if an error occurs.
|
||||
|
||||
@param webpData The WebP data.
|
||||
@param decodeForDisplay If YES, this method will decode the image and convert it
|
||||
to BGRA8888 (premultiplied) format for CALayer display.
|
||||
@param useThreads YES to enable multi-thread decode.
|
||||
(speed up, but cost more CPU)
|
||||
@param bypassFiltering YES to skip the in-loop filtering.
|
||||
(speed up, but may lose some smooth)
|
||||
@param noFancyUpsampling YES to use faster pointwise upsampler.
|
||||
(speed down, and may lose some details).
|
||||
@return The decoded image, or NULL if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CGImageRef _Nullable YYCGImageCreateWithWebPData(CFDataRef webpData,
|
||||
BOOL decodeForDisplay,
|
||||
BOOL useThreads,
|
||||
BOOL bypassFiltering,
|
||||
BOOL noFancyUpsampling);
|
||||
|
||||
typedef NS_ENUM(NSUInteger, YYImagePreset) {
|
||||
YYImagePresetDefault = 0, ///< default preset.
|
||||
YYImagePresetPicture, ///< digital picture, like portrait, inner shot
|
||||
YYImagePresetPhoto, ///< outdoor photograph, with natural lighting
|
||||
YYImagePresetDrawing, ///< hand or line drawing, with high-contrast details
|
||||
YYImagePresetIcon, ///< small-sized colorful images
|
||||
YYImagePresetText ///< text-like
|
||||
};
|
||||
|
||||
/**
|
||||
Encode a CGImage to WebP data
|
||||
|
||||
@param imageRef image
|
||||
@param lossless YES=lossless (similar to PNG), NO=lossy (similar to JPEG)
|
||||
@param quality 0.0~1.0 (0=smallest file, 1.0=biggest file)
|
||||
For lossless image, try the value near 1.0; for lossy, try the value near 0.8.
|
||||
@param compressLevel 0~6 (0=fast, 6=slower-better). Default is 4.
|
||||
@param preset Preset for different image type, default is YYImagePresetDefault.
|
||||
@return WebP data, or nil if an error occurs.
|
||||
*/
|
||||
CG_EXTERN CFDataRef _Nullable YYCGImageCreateEncodedWebPData(CGImageRef imageRef,
|
||||
BOOL lossless,
|
||||
CGFloat quality,
|
||||
int compressLevel,
|
||||
YYImagePreset preset);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// YYSpriteImage.h
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 15/4/21.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#if __has_include(<YYImage/YYImage.h>)
|
||||
#import <YYImage/YYAnimatedImageView.h>
|
||||
#elif __has_include(<YYWebImage/YYImage.h>)
|
||||
#import <YYWebImage/YYAnimatedImageView.h>
|
||||
#else
|
||||
#import "YYAnimatedImageView.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An image to display sprite sheet animation.
|
||||
|
||||
@discussion It is a fully compatible `UIImage` subclass.
|
||||
The animation can be played by YYAnimatedImageView.
|
||||
|
||||
Sample Code:
|
||||
|
||||
// 8 * 12 sprites in a single sheet image
|
||||
UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"];
|
||||
NSMutableArray *contentRects = [NSMutableArray new];
|
||||
NSMutableArray *durations = [NSMutableArray new];
|
||||
for (int j = 0; j < 12; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
CGRect rect;
|
||||
rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
rect.origin.x = img.size.width / 8 * i;
|
||||
rect.origin.y = img.size.height / 12 * j;
|
||||
[contentRects addObject:[NSValue valueWithCGRect:rect]];
|
||||
[durations addObject:@(1 / 60.0)];
|
||||
}
|
||||
}
|
||||
YYSpriteSheetImage *sprite;
|
||||
sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img
|
||||
contentRects:contentRects
|
||||
frameDurations:durations
|
||||
loopCount:0];
|
||||
YYAnimatedImageView *imgView = [YYAnimatedImageView new];
|
||||
imgView.size = CGSizeMake(img.size.width / 8, img.size.height / 12);
|
||||
imgView.image = sprite;
|
||||
|
||||
|
||||
|
||||
@discussion It can also be used to display single frame in sprite sheet image.
|
||||
Sample Code:
|
||||
|
||||
YYSpriteSheetImage *sheet = ...;
|
||||
UIImageView *imageView = ...;
|
||||
imageView.image = sheet;
|
||||
imageView.layer.contentsRect = [sheet contentsRectForCALayerAtIndex:6];
|
||||
|
||||
*/
|
||||
@interface YYSpriteSheetImage : UIImage <YYAnimatedImage>
|
||||
|
||||
/**
|
||||
Creates and returns an image object.
|
||||
|
||||
@param image The sprite sheet image (contains all frames).
|
||||
|
||||
@param contentRects The sprite sheet image frame rects in the image coordinates.
|
||||
The rectangle should not outside the image's bounds. The objects in this array
|
||||
should be created with [NSValue valueWithCGRect:].
|
||||
|
||||
@param frameDurations The sprite sheet image frame's durations in seconds.
|
||||
The objects in this array should be NSNumber.
|
||||
|
||||
@param loopCount Animation loop count, 0 means infinite looping.
|
||||
|
||||
@return An image object, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable instancetype)initWithSpriteSheetImage:(UIImage *)image
|
||||
contentRects:(NSArray<NSValue *> *)contentRects
|
||||
frameDurations:(NSArray<NSNumber *> *)frameDurations
|
||||
loopCount:(NSUInteger)loopCount;
|
||||
|
||||
@property (nonatomic, readonly) NSArray<NSValue *> *contentRects;
|
||||
@property (nonatomic, readonly) NSArray<NSValue *> *frameDurations;
|
||||
@property (nonatomic, readonly) NSUInteger loopCount;
|
||||
|
||||
/**
|
||||
Get the contents rect for CALayer.
|
||||
See "contentsRect" property in CALayer for more information.
|
||||
|
||||
@param index Index of frame.
|
||||
@return Contents Rect.
|
||||
*/
|
||||
- (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// YYSpriteImage.m
|
||||
// YYImage <https://github.com/ibireme/YYImage>
|
||||
//
|
||||
// Created by ibireme on 15/4/21.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYSpriteSheetImage.h"
|
||||
|
||||
@implementation YYSpriteSheetImage
|
||||
|
||||
- (instancetype)initWithSpriteSheetImage:(UIImage *)image
|
||||
contentRects:(NSArray *)contentRects
|
||||
frameDurations:(NSArray *)frameDurations
|
||||
loopCount:(NSUInteger)loopCount {
|
||||
if (!image.CGImage) return nil;
|
||||
if (contentRects.count < 1 || frameDurations.count < 1) return nil;
|
||||
if (contentRects.count != frameDurations.count) return nil;
|
||||
|
||||
self = [super initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
|
||||
if (!self) return nil;
|
||||
|
||||
_contentRects = contentRects.copy;
|
||||
_frameDurations = frameDurations.copy;
|
||||
_loopCount = loopCount;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index {
|
||||
CGRect layerRect = CGRectMake(0, 0, 1, 1);
|
||||
if (index >= _contentRects.count) return layerRect;
|
||||
|
||||
CGSize imageSize = self.size;
|
||||
CGRect rect = [self animatedImageContentsRectAtIndex:index];
|
||||
if (imageSize.width > 0.01 && imageSize.height > 0.01) {
|
||||
layerRect.origin.x = rect.origin.x / imageSize.width;
|
||||
layerRect.origin.y = rect.origin.y / imageSize.height;
|
||||
layerRect.size.width = rect.size.width / imageSize.width;
|
||||
layerRect.size.height = rect.size.height / imageSize.height;
|
||||
layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1));
|
||||
if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) {
|
||||
layerRect = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
return layerRect;
|
||||
}
|
||||
|
||||
#pragma mark @protocol YYAnimatedImage
|
||||
|
||||
- (NSUInteger)animatedImageFrameCount {
|
||||
return _contentRects.count;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageLoopCount {
|
||||
return _loopCount;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageBytesPerFrame {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
|
||||
if (index >= _frameDurations.count) return 0;
|
||||
return ((NSNumber *)_frameDurations[index]).doubleValue;
|
||||
}
|
||||
|
||||
- (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index {
|
||||
if (index >= _contentRects.count) return CGRectZero;
|
||||
return ((NSValue *)_contentRects[index]).CGRectValue;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,30 @@
|
|||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# WebP Codec
|
||||
|
||||
```
|
||||
__ __ ____ ____ ____
|
||||
/ \\/ \/ _ \/ _ )/ _ \
|
||||
\ / __/ _ \ __/
|
||||
\__\__/\____/\_____/__/ ____ ___
|
||||
/ _/ / \ \ / _ \/ _/
|
||||
/ \_/ / / \ \ __/ \__
|
||||
\____/____/\_____/_____/____/v1.3.2
|
||||
```
|
||||
|
||||
WebP codec is a library to encode and decode images in WebP format. This package
|
||||
contains the library that can be used in other programs to add WebP support, as
|
||||
well as the command line tools 'cwebp' and 'dwebp' to compress and decompress
|
||||
images respectively.
|
||||
|
||||
See https://developers.google.com/speed/webp for details on the image format.
|
||||
|
||||
The latest source tree is available at
|
||||
https://chromium.googlesource.com/webm/libwebp
|
||||
|
||||
It is released under the same license as the WebM project. See
|
||||
https://www.webmproject.org/license/software/ or the "COPYING" file for details.
|
||||
An additional intellectual property rights grant can be found in the file
|
||||
PATENTS.
|
||||
|
||||
## Building
|
||||
|
||||
See the [building documentation](doc/building.md).
|
||||
|
||||
## Encoding and Decoding Tools
|
||||
|
||||
The examples/ directory contains tools to encode and decode images and
|
||||
animations, view information about WebP images, and more. See the
|
||||
[tools documentation](doc/tools.md).
|
||||
|
||||
## APIs
|
||||
|
||||
See the [APIs documentation](doc/api.md), and API usage examples in the
|
||||
`examples/` directory.
|
||||
|
||||
## Bugs
|
||||
|
||||
Please report all bugs to the issue tracker: https://bugs.chromium.org/p/webp
|
||||
|
||||
Patches welcome! See [how to contribute](CONTRIBUTING.md).
|
||||
|
||||
## Discuss
|
||||
|
||||
Email: webp-discuss@webmproject.org
|
||||
|
||||
Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
|
|
@ -0,0 +1,41 @@
|
|||
AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
|
||||
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||
|
||||
lib_LTLIBRARIES = libsharpyuv.la
|
||||
|
||||
noinst_LTLIBRARIES =
|
||||
noinst_LTLIBRARIES += libsharpyuv_sse2.la
|
||||
noinst_LTLIBRARIES += libsharpyuv_neon.la
|
||||
|
||||
libsharpyuvinclude_HEADERS =
|
||||
libsharpyuvinclude_HEADERS += sharpyuv.h
|
||||
libsharpyuvinclude_HEADERS += sharpyuv_csp.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += ../src/dsp/cpu.c
|
||||
noinst_HEADERS += ../src/dsp/cpu.h
|
||||
noinst_HEADERS += ../src/webp/types.h
|
||||
|
||||
libsharpyuv_sse2_la_SOURCES =
|
||||
libsharpyuv_sse2_la_SOURCES += sharpyuv_sse2.c
|
||||
libsharpyuv_sse2_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
|
||||
libsharpyuv_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
|
||||
|
||||
libsharpyuv_neon_la_SOURCES =
|
||||
libsharpyuv_neon_la_SOURCES += sharpyuv_neon.c
|
||||
libsharpyuv_neon_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
|
||||
libsharpyuv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
|
||||
|
||||
libsharpyuv_la_SOURCES =
|
||||
libsharpyuv_la_SOURCES += sharpyuv_cpu.c sharpyuv_cpu.h
|
||||
libsharpyuv_la_SOURCES += sharpyuv_csp.c sharpyuv_csp.h
|
||||
libsharpyuv_la_SOURCES += sharpyuv_dsp.c sharpyuv_dsp.h
|
||||
libsharpyuv_la_SOURCES += sharpyuv_gamma.c sharpyuv_gamma.h
|
||||
libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h
|
||||
|
||||
libsharpyuv_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libsharpyuv_la_LDFLAGS = -no-undefined -version-info 0:1:0 -lm
|
||||
libsharpyuv_la_LIBADD =
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_sse2.la
|
||||
libsharpyuv_la_LIBADD += libsharpyuv_neon.la
|
||||
libsharpyuvincludedir = $(includedir)/webp/sharpyuv
|
||||
pkgconfig_DATA = libsharpyuv.pc
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/webp
|
||||
|
||||
Name: libsharpyuv
|
||||
Description: Library for sharp RGB to YUV conversion
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@webp_libname_prefix@sharpyuv
|
||||
Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
|
|
@ -0,0 +1,41 @@
|
|||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,2,1
|
||||
PRODUCTVERSION 0,0,2,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libsharpyuv DLL"
|
||||
VALUE "FileVersion", "0.2.1"
|
||||
VALUE "InternalName", "libsharpyuv.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2023"
|
||||
VALUE "OriginalFilename", "libsharpyuv.dll"
|
||||
VALUE "ProductName", "SharpYuv Library"
|
||||
VALUE "ProductVersion", "0.2.1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
|
@ -0,0 +1,527 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Sharp RGB to YUV conversion.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/webp/types.h"
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
#include "sharpyuv/sharpyuv_gamma.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int SharpYuvGetVersion(void) {
|
||||
return SHARPYUV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Sharp RGB->YUV conversion
|
||||
|
||||
static const int kNumIterations = 4;
|
||||
|
||||
#define YUV_FIX 16 // fixed-point precision for RGB->YUV
|
||||
static const int kYuvHalf = 1 << (YUV_FIX - 1);
|
||||
|
||||
// Max bit depth so that intermediate calculations fit in 16 bits.
|
||||
static const int kMaxBitDepth = 14;
|
||||
|
||||
// Returns the precision shift to use based on the input rgb_bit_depth.
|
||||
static int GetPrecisionShift(int rgb_bit_depth) {
|
||||
// Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove
|
||||
// bits if needed.
|
||||
return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2
|
||||
: (kMaxBitDepth - rgb_bit_depth);
|
||||
}
|
||||
|
||||
typedef int16_t fixed_t; // signed type with extra precision for UV
|
||||
typedef uint16_t fixed_y_t; // unsigned type with extra precision for W
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint8_t clip_8b(fixed_t v) {
|
||||
return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
|
||||
}
|
||||
|
||||
static uint16_t clip(fixed_t v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
}
|
||||
|
||||
static fixed_y_t clip_bit_depth(int y, int bit_depth) {
|
||||
const int max = (1 << bit_depth) - 1;
|
||||
return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int RGBToGray(int64_t r, int64_t g, int64_t b) {
|
||||
const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf;
|
||||
return (int)(luma >> YUV_FIX);
|
||||
}
|
||||
|
||||
static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||
int rgb_bit_depth) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
const uint32_t A = SharpYuvGammaToLinear(a, bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(b, bit_depth);
|
||||
const uint32_t C = SharpYuvGammaToLinear(c, bit_depth);
|
||||
const uint32_t D = SharpYuvGammaToLinear(d, bit_depth);
|
||||
return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
|
||||
int rgb_bit_depth) {
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth);
|
||||
const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth);
|
||||
const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth);
|
||||
const uint32_t Y = RGBToGray(R, G, B);
|
||||
dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
|
||||
fixed_t* dst, int uv_w, int rgb_bit_depth) {
|
||||
int i;
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int r =
|
||||
ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
|
||||
src2[0 * uv_w + 1], rgb_bit_depth);
|
||||
const int g =
|
||||
ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
|
||||
src2[2 * uv_w + 1], rgb_bit_depth);
|
||||
const int b =
|
||||
ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
|
||||
src2[4 * uv_w + 1], rgb_bit_depth);
|
||||
const int W = RGBToGray(r, g, b);
|
||||
dst[0 * uv_w] = (fixed_t)(r - W);
|
||||
dst[1 * uv_w] = (fixed_t)(g - W);
|
||||
dst[2 * uv_w] = (fixed_t)(b - W);
|
||||
dst += 1;
|
||||
src1 += 2;
|
||||
src2 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
|
||||
int i;
|
||||
assert(w > 0);
|
||||
for (i = 0; i < w; ++i) {
|
||||
y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) {
|
||||
const int v0 = (A * 3 + B + 2) >> 2;
|
||||
return clip_bit_depth(v0 + W0, bit_depth);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static WEBP_INLINE int Shift(int v, int shift) {
|
||||
return (shift >= 0) ? (v << shift) : (v >> -shift);
|
||||
}
|
||||
|
||||
static void ImportOneRow(const uint8_t* const r_ptr,
|
||||
const uint8_t* const g_ptr,
|
||||
const uint8_t* const b_ptr,
|
||||
int rgb_step,
|
||||
int rgb_bit_depth,
|
||||
int pic_width,
|
||||
fixed_y_t* const dst) {
|
||||
// Convert the rgb_step from a number of bytes to a number of uint8_t or
|
||||
// uint16_t values depending the bit depth.
|
||||
const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;
|
||||
int i;
|
||||
const int w = (pic_width + 1) & ~1;
|
||||
for (i = 0; i < pic_width; ++i) {
|
||||
const int off = i * step;
|
||||
const int shift = GetPrecisionShift(rgb_bit_depth);
|
||||
if (rgb_bit_depth == 8) {
|
||||
dst[i + 0 * w] = Shift(r_ptr[off], shift);
|
||||
dst[i + 1 * w] = Shift(g_ptr[off], shift);
|
||||
dst[i + 2 * w] = Shift(b_ptr[off], shift);
|
||||
} else {
|
||||
dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);
|
||||
dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
|
||||
dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
|
||||
}
|
||||
}
|
||||
if (pic_width & 1) { // replicate rightmost pixel
|
||||
dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
|
||||
dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
|
||||
dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void InterpolateTwoRows(const fixed_y_t* const best_y,
|
||||
const fixed_t* prev_uv,
|
||||
const fixed_t* cur_uv,
|
||||
const fixed_t* next_uv,
|
||||
int w,
|
||||
fixed_y_t* out1,
|
||||
fixed_y_t* out2,
|
||||
int rgb_bit_depth) {
|
||||
const int uv_w = w >> 1;
|
||||
const int len = (w - 1) >> 1; // length to filter
|
||||
int k = 3;
|
||||
const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
|
||||
while (k-- > 0) { // process each R/G/B segments in turn
|
||||
// special boundary case for i==0
|
||||
out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);
|
||||
out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);
|
||||
|
||||
SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1,
|
||||
bit_depth);
|
||||
SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1,
|
||||
bit_depth);
|
||||
|
||||
// special boundary case for i == w - 1 when w is even
|
||||
if (!(w & 1)) {
|
||||
out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
|
||||
best_y[w - 1 + 0], bit_depth);
|
||||
out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
|
||||
best_y[w - 1 + w], bit_depth);
|
||||
}
|
||||
out1 += w;
|
||||
out2 += w;
|
||||
prev_uv += uv_w;
|
||||
cur_uv += uv_w;
|
||||
next_uv += uv_w;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,
|
||||
const int coeffs[4], int sfix) {
|
||||
const int srounder = 1 << (YUV_FIX + sfix - 1);
|
||||
const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
|
||||
coeffs[3] + srounder;
|
||||
return (luma >> (YUV_FIX + sfix));
|
||||
}
|
||||
|
||||
static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
|
||||
uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,
|
||||
int u_stride, uint8_t* v_ptr, int v_stride,
|
||||
int rgb_bit_depth,
|
||||
int yuv_bit_depth, int width, int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
int i, j;
|
||||
const fixed_t* const best_uv_base = best_uv;
|
||||
const int w = (width + 1) & ~1;
|
||||
const int h = (height + 1) & ~1;
|
||||
const int uv_w = w >> 1;
|
||||
const int uv_h = h >> 1;
|
||||
const int sfix = GetPrecisionShift(rgb_bit_depth);
|
||||
const int yuv_max = (1 << yuv_bit_depth) - 1;
|
||||
|
||||
for (best_uv = best_uv_base, j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
const int off = (i >> 1);
|
||||
const int W = best_y[i];
|
||||
const int r = best_uv[off + 0 * uv_w] + W;
|
||||
const int g = best_uv[off + 1 * uv_w] + W;
|
||||
const int b = best_uv[off + 2 * uv_w] + W;
|
||||
const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix);
|
||||
if (yuv_bit_depth <= 8) {
|
||||
y_ptr[i] = clip_8b(y);
|
||||
} else {
|
||||
((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
|
||||
}
|
||||
}
|
||||
best_y += w;
|
||||
best_uv += (j & 1) * 3 * uv_w;
|
||||
y_ptr += y_stride;
|
||||
}
|
||||
for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
|
||||
for (i = 0; i < uv_w; ++i) {
|
||||
const int off = i;
|
||||
// Note r, g and b values here are off by W, but a constant offset on all
|
||||
// 3 components doesn't change the value of u and v with a YCbCr matrix.
|
||||
const int r = best_uv[off + 0 * uv_w];
|
||||
const int g = best_uv[off + 1 * uv_w];
|
||||
const int b = best_uv[off + 2 * uv_w];
|
||||
const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);
|
||||
const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);
|
||||
if (yuv_bit_depth <= 8) {
|
||||
u_ptr[i] = clip_8b(u);
|
||||
v_ptr[i] = clip_8b(v);
|
||||
} else {
|
||||
((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
|
||||
((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
|
||||
}
|
||||
}
|
||||
best_uv += 3 * uv_w;
|
||||
u_ptr += u_stride;
|
||||
v_ptr += v_stride;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main function
|
||||
|
||||
static void* SafeMalloc(uint64_t nmemb, size_t size) {
|
||||
const uint64_t total_size = nmemb * (uint64_t)size;
|
||||
if (total_size != (size_t)total_size) return NULL;
|
||||
return malloc((size_t)total_size);
|
||||
}
|
||||
|
||||
#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((W) * (H), sizeof(T)))
|
||||
|
||||
static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
|
||||
const uint8_t* b_ptr, int rgb_step, int rgb_stride,
|
||||
int rgb_bit_depth, uint8_t* y_ptr, int y_stride,
|
||||
uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,
|
||||
int v_stride, int yuv_bit_depth, int width,
|
||||
int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
// we expand the right/bottom border if needed
|
||||
const int w = (width + 1) & ~1;
|
||||
const int h = (height + 1) & ~1;
|
||||
const int uv_w = w >> 1;
|
||||
const int uv_h = h >> 1;
|
||||
uint64_t prev_diff_y_sum = ~0;
|
||||
int j, iter;
|
||||
|
||||
// TODO(skal): allocate one big memory chunk. But for now, it's easier
|
||||
// for valgrind debugging to have several chunks.
|
||||
fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch
|
||||
fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
|
||||
fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
|
||||
fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
|
||||
fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
|
||||
fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
|
||||
fixed_y_t* best_y = best_y_base;
|
||||
fixed_y_t* target_y = target_y_base;
|
||||
fixed_t* best_uv = best_uv_base;
|
||||
fixed_t* target_uv = target_uv_base;
|
||||
const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
|
||||
int ok;
|
||||
assert(w > 0);
|
||||
assert(h > 0);
|
||||
|
||||
if (best_y_base == NULL || best_uv_base == NULL ||
|
||||
target_y_base == NULL || target_uv_base == NULL ||
|
||||
best_rgb_y == NULL || best_rgb_uv == NULL ||
|
||||
tmp_buffer == NULL) {
|
||||
ok = 0;
|
||||
goto End;
|
||||
}
|
||||
|
||||
// Import RGB samples to W/RGB representation.
|
||||
for (j = 0; j < height; j += 2) {
|
||||
const int is_last_row = (j == height - 1);
|
||||
fixed_y_t* const src1 = tmp_buffer + 0 * w;
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
|
||||
// prepare two rows of input
|
||||
ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,
|
||||
src1);
|
||||
if (!is_last_row) {
|
||||
ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
|
||||
rgb_step, rgb_bit_depth, width, src2);
|
||||
} else {
|
||||
memcpy(src2, src1, 3 * w * sizeof(*src2));
|
||||
}
|
||||
StoreGray(src1, best_y + 0, w);
|
||||
StoreGray(src2, best_y + w, w);
|
||||
|
||||
UpdateW(src1, target_y, w, rgb_bit_depth);
|
||||
UpdateW(src2, target_y + w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth);
|
||||
memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
target_y += 2 * w;
|
||||
target_uv += 3 * uv_w;
|
||||
r_ptr += 2 * rgb_stride;
|
||||
g_ptr += 2 * rgb_stride;
|
||||
b_ptr += 2 * rgb_stride;
|
||||
}
|
||||
|
||||
// Iterate and resolve clipping conflicts.
|
||||
for (iter = 0; iter < kNumIterations; ++iter) {
|
||||
const fixed_t* cur_uv = best_uv_base;
|
||||
const fixed_t* prev_uv = best_uv_base;
|
||||
uint64_t diff_y_sum = 0;
|
||||
|
||||
best_y = best_y_base;
|
||||
best_uv = best_uv_base;
|
||||
target_y = target_y_base;
|
||||
target_uv = target_uv_base;
|
||||
for (j = 0; j < h; j += 2) {
|
||||
fixed_y_t* const src1 = tmp_buffer + 0 * w;
|
||||
fixed_y_t* const src2 = tmp_buffer + 3 * w;
|
||||
{
|
||||
const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
|
||||
InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,
|
||||
src1, src2, rgb_bit_depth);
|
||||
prev_uv = cur_uv;
|
||||
cur_uv = next_uv;
|
||||
}
|
||||
|
||||
UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth);
|
||||
UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth);
|
||||
UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth);
|
||||
|
||||
// update two rows of Y and one row of RGB
|
||||
diff_y_sum +=
|
||||
SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w,
|
||||
rgb_bit_depth + GetPrecisionShift(rgb_bit_depth));
|
||||
SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
|
||||
|
||||
best_y += 2 * w;
|
||||
best_uv += 3 * uv_w;
|
||||
target_y += 2 * w;
|
||||
target_uv += 3 * uv_w;
|
||||
}
|
||||
// test exit condition
|
||||
if (iter > 0) {
|
||||
if (diff_y_sum < diff_y_threshold) break;
|
||||
if (diff_y_sum > prev_diff_y_sum) break;
|
||||
}
|
||||
prev_diff_y_sum = diff_y_sum;
|
||||
}
|
||||
|
||||
// final reconstruction
|
||||
ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr,
|
||||
u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
|
||||
width, height, yuv_matrix);
|
||||
|
||||
End:
|
||||
free(best_y_base);
|
||||
free(best_uv_base);
|
||||
free(target_y_base);
|
||||
free(target_uv_base);
|
||||
free(best_rgb_y);
|
||||
free(best_rgb_uv);
|
||||
free(tmp_buffer);
|
||||
return ok;
|
||||
}
|
||||
#undef SAFE_ALLOC
|
||||
|
||||
#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
|
||||
#include <pthread.h> // NOLINT
|
||||
|
||||
#define LOCK_ACCESS \
|
||||
static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
|
||||
if (pthread_mutex_lock(&sharpyuv_lock)) return
|
||||
#define UNLOCK_ACCESS_AND_RETURN \
|
||||
do { \
|
||||
(void)pthread_mutex_unlock(&sharpyuv_lock); \
|
||||
return; \
|
||||
} while (0)
|
||||
#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
|
||||
#define LOCK_ACCESS do {} while (0)
|
||||
#define UNLOCK_ACCESS_AND_RETURN return
|
||||
#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
|
||||
|
||||
// Hidden exported init function.
|
||||
// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
|
||||
// users can declare it as extern and call it with an alternate VP8CPUInfo
|
||||
// function.
|
||||
extern VP8CPUInfo SharpYuvGetCPUInfo;
|
||||
SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
|
||||
void SharpYuvInit(VP8CPUInfo cpu_info_func) {
|
||||
static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&sharpyuv_last_cpuinfo_used;
|
||||
LOCK_ACCESS;
|
||||
// Only update SharpYuvGetCPUInfo when called from external code to avoid a
|
||||
// race on reading the value in SharpYuvConvert().
|
||||
if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {
|
||||
SharpYuvGetCPUInfo = cpu_info_func;
|
||||
}
|
||||
if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {
|
||||
UNLOCK_ACCESS_AND_RETURN;
|
||||
}
|
||||
|
||||
SharpYuvInitDsp();
|
||||
SharpYuvInitGammaTables();
|
||||
|
||||
sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo;
|
||||
UNLOCK_ACCESS_AND_RETURN;
|
||||
}
|
||||
|
||||
int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step, int rgb_stride,
|
||||
int rgb_bit_depth, void* y_ptr, int y_stride,
|
||||
void* u_ptr, int u_stride, void* v_ptr,
|
||||
int v_stride, int yuv_bit_depth, int width,
|
||||
int height, const SharpYuvConversionMatrix* yuv_matrix) {
|
||||
SharpYuvConversionMatrix scaled_matrix;
|
||||
const int rgb_max = (1 << rgb_bit_depth) - 1;
|
||||
const int rgb_round = 1 << (rgb_bit_depth - 1);
|
||||
const int yuv_max = (1 << yuv_bit_depth) - 1;
|
||||
const int sfix = GetPrecisionShift(rgb_bit_depth);
|
||||
|
||||
if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
|
||||
r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL ||
|
||||
u_ptr == NULL || v_ptr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 &&
|
||||
rgb_bit_depth != 16) {
|
||||
return 0;
|
||||
}
|
||||
if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {
|
||||
return 0;
|
||||
}
|
||||
if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride %2 != 0)) {
|
||||
// Step/stride should be even for uint16_t buffers.
|
||||
return 0;
|
||||
}
|
||||
if (yuv_bit_depth > 8 &&
|
||||
(y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) {
|
||||
// Stride should be even for uint16_t buffers.
|
||||
return 0;
|
||||
}
|
||||
// The address of the function pointer is used to avoid a read race.
|
||||
SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo);
|
||||
|
||||
// Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the
|
||||
// rgb->yuv conversion matrix.
|
||||
if (rgb_bit_depth == yuv_bit_depth) {
|
||||
memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix));
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
scaled_matrix.rgb_to_y[i] =
|
||||
(yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max;
|
||||
scaled_matrix.rgb_to_u[i] =
|
||||
(yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max;
|
||||
scaled_matrix.rgb_to_v[i] =
|
||||
(yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max;
|
||||
}
|
||||
}
|
||||
// Also incorporate precision change scaling.
|
||||
scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix);
|
||||
scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix);
|
||||
scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix);
|
||||
|
||||
return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride,
|
||||
rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride,
|
||||
v_ptr, v_stride, yuv_bit_depth, width, height,
|
||||
&scaled_matrix);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Sharp RGB to YUV conversion.
|
||||
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef SHARPYUV_EXTERN
|
||||
#ifdef WEBP_EXTERN
|
||||
#define SHARPYUV_EXTERN WEBP_EXTERN
|
||||
#else
|
||||
// This explicitly marks library functions and allows for changing the
|
||||
// signature for e.g., Windows DLL builds.
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define SHARPYUV_EXTERN extern __attribute__((visibility("default")))
|
||||
#else
|
||||
#if defined(_MSC_VER) && defined(WEBP_DLL)
|
||||
#define SHARPYUV_EXTERN __declspec(dllexport)
|
||||
#else
|
||||
#define SHARPYUV_EXTERN extern
|
||||
#endif /* _MSC_VER && WEBP_DLL */
|
||||
#endif /* __GNUC__ >= 4 */
|
||||
#endif /* WEBP_EXTERN */
|
||||
#endif /* SHARPYUV_EXTERN */
|
||||
|
||||
// SharpYUV API version following the convention from semver.org
|
||||
#define SHARPYUV_VERSION_MAJOR 0
|
||||
#define SHARPYUV_VERSION_MINOR 2
|
||||
#define SHARPYUV_VERSION_PATCH 1
|
||||
// Version as a uint32_t. The major number is the high 8 bits.
|
||||
// The minor number is the middle 8 bits. The patch number is the low 16 bits.
|
||||
#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
|
||||
(((MAJOR) << 24) | ((MINOR) << 16) | (PATCH))
|
||||
#define SHARPYUV_VERSION \
|
||||
SHARPYUV_MAKE_VERSION(SHARPYUV_VERSION_MAJOR, SHARPYUV_VERSION_MINOR, \
|
||||
SHARPYUV_VERSION_PATCH)
|
||||
|
||||
// Returns the library's version number, packed in hexadecimal. See
|
||||
// SHARPYUV_VERSION.
|
||||
SHARPYUV_EXTERN int SharpYuvGetVersion(void);
|
||||
|
||||
// RGB to YUV conversion matrix, in 16 bit fixed point.
|
||||
// y = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
|
||||
// u = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
|
||||
// v = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
|
||||
// Then y, u and v values are divided by 1<<16 and rounded.
|
||||
typedef struct {
|
||||
int rgb_to_y[4];
|
||||
int rgb_to_u[4];
|
||||
int rgb_to_v[4];
|
||||
} SharpYuvConversionMatrix;
|
||||
|
||||
// Converts RGB to YUV420 using a downsampling algorithm that minimizes
|
||||
// artefacts caused by chroma subsampling.
|
||||
// This is slower than standard downsampling (averaging of 4 UV values).
|
||||
// Assumes that the image will be upsampled using a bilinear filter. If nearest
|
||||
// neighbor is used instead, the upsampled image might look worse than with
|
||||
// standard downsampling.
|
||||
// r_ptr, g_ptr, b_ptr: pointers to the source r, g and b channels. Should point
|
||||
// to uint8_t buffers if rgb_bit_depth is 8, or uint16_t buffers otherwise.
|
||||
// rgb_step: distance in bytes between two horizontally adjacent pixels on the
|
||||
// r, g and b channels. If rgb_bit_depth is > 8, it should be a
|
||||
// multiple of 2.
|
||||
// rgb_stride: distance in bytes between two vertically adjacent pixels on the
|
||||
// r, g, and b channels. If rgb_bit_depth is > 8, it should be a
|
||||
// multiple of 2.
|
||||
// rgb_bit_depth: number of bits for each r/g/b value. One of: 8, 10, 12, 16.
|
||||
// Note: 16 bit input is truncated to 14 bits before conversion to yuv.
|
||||
// yuv_bit_depth: number of bits for each y/u/v value. One of: 8, 10, 12.
|
||||
// y_ptr, u_ptr, v_ptr: pointers to the destination y, u and v channels. Should
|
||||
// point to uint8_t buffers if yuv_bit_depth is 8, or uint16_t buffers
|
||||
// otherwise.
|
||||
// y_stride, u_stride, v_stride: distance in bytes between two vertically
|
||||
// adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they
|
||||
// should be multiples of 2.
|
||||
// width, height: width and height of the image in pixels
|
||||
SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
|
||||
const void* b_ptr, int rgb_step,
|
||||
int rgb_stride, int rgb_bit_depth,
|
||||
void* y_ptr, int y_stride, void* u_ptr,
|
||||
int u_stride, void* v_ptr, int v_stride,
|
||||
int yuv_bit_depth, int width, int height,
|
||||
const SharpYuvConversionMatrix* yuv_matrix);
|
||||
|
||||
// TODO(b/194336375): Add YUV444 to YUV420 conversion. Maybe also add 422
|
||||
// support (it's rarely used in practice, especially for images).
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_SHARPYUV_SHARPYUV_H_
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
|
||||
// Include src/dsp/cpu.c to create SharpYuvGetCPUInfo from VP8GetCPUInfo. The
|
||||
// function pointer is renamed in sharpyuv_cpu.h.
|
||||
#include "src/dsp/cpu.c"
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_CPU_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_CPU_H_
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
|
||||
// Avoid exporting SharpYuvGetCPUInfo in shared object / DLL builds.
|
||||
// SharpYuvInit() replaces the use of the function pointer.
|
||||
#undef WEBP_EXTERN
|
||||
#define WEBP_EXTERN extern
|
||||
#define VP8GetCPUInfo SharpYuvGetCPUInfo
|
||||
#include "src/dsp/cpu.h"
|
||||
|
||||
#endif // WEBP_SHARPYUV_SHARPYUV_CPU_H_
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Colorspace utilities.
|
||||
|
||||
#include "sharpyuv/sharpyuv_csp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static int ToFixed16(float f) { return (int)floor(f * (1 << 16) + 0.5f); }
|
||||
|
||||
void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
|
||||
SharpYuvConversionMatrix* matrix) {
|
||||
const float kr = yuv_color_space->kr;
|
||||
const float kb = yuv_color_space->kb;
|
||||
const float kg = 1.0f - kr - kb;
|
||||
const float cr = 0.5f / (1.0f - kb);
|
||||
const float cb = 0.5f / (1.0f - kr);
|
||||
|
||||
const int shift = yuv_color_space->bit_depth - 8;
|
||||
|
||||
const float denom = (float)((1 << yuv_color_space->bit_depth) - 1);
|
||||
float scale_y = 1.0f;
|
||||
float add_y = 0.0f;
|
||||
float scale_u = cr;
|
||||
float scale_v = cb;
|
||||
float add_uv = (float)(128 << shift);
|
||||
assert(yuv_color_space->bit_depth >= 8);
|
||||
|
||||
if (yuv_color_space->range == kSharpYuvRangeLimited) {
|
||||
scale_y *= (219 << shift) / denom;
|
||||
scale_u *= (224 << shift) / denom;
|
||||
scale_v *= (224 << shift) / denom;
|
||||
add_y = (float)(16 << shift);
|
||||
}
|
||||
|
||||
matrix->rgb_to_y[0] = ToFixed16(kr * scale_y);
|
||||
matrix->rgb_to_y[1] = ToFixed16(kg * scale_y);
|
||||
matrix->rgb_to_y[2] = ToFixed16(kb * scale_y);
|
||||
matrix->rgb_to_y[3] = ToFixed16(add_y);
|
||||
|
||||
matrix->rgb_to_u[0] = ToFixed16(-kr * scale_u);
|
||||
matrix->rgb_to_u[1] = ToFixed16(-kg * scale_u);
|
||||
matrix->rgb_to_u[2] = ToFixed16((1 - kb) * scale_u);
|
||||
matrix->rgb_to_u[3] = ToFixed16(add_uv);
|
||||
|
||||
matrix->rgb_to_v[0] = ToFixed16((1 - kr) * scale_v);
|
||||
matrix->rgb_to_v[1] = ToFixed16(-kg * scale_v);
|
||||
matrix->rgb_to_v[2] = ToFixed16(-kb * scale_v);
|
||||
matrix->rgb_to_v[3] = ToFixed16(add_uv);
|
||||
}
|
||||
|
||||
// Matrices are in YUV_FIX fixed point precision.
|
||||
// WebP's matrix, similar but not identical to kRec601LimitedMatrix.
|
||||
static const SharpYuvConversionMatrix kWebpMatrix = {
|
||||
{16839, 33059, 6420, 16 << 16},
|
||||
{-9719, -19081, 28800, 128 << 16},
|
||||
{28800, -24116, -4684, 128 << 16},
|
||||
};
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec601LimitedMatrix = {
|
||||
{16829, 33039, 6416, 16 << 16},
|
||||
{-9714, -19071, 28784, 128 << 16},
|
||||
{28784, -24103, -4681, 128 << 16},
|
||||
};
|
||||
// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec601FullMatrix = {
|
||||
{19595, 38470, 7471, 0},
|
||||
{-11058, -21710, 32768, 128 << 16},
|
||||
{32768, -27439, -5329, 128 << 16},
|
||||
};
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeLimited
|
||||
static const SharpYuvConversionMatrix kRec709LimitedMatrix = {
|
||||
{11966, 40254, 4064, 16 << 16},
|
||||
{-6596, -22189, 28784, 128 << 16},
|
||||
{28784, -26145, -2639, 128 << 16},
|
||||
};
|
||||
// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeFull
|
||||
static const SharpYuvConversionMatrix kRec709FullMatrix = {
|
||||
{13933, 46871, 4732, 0},
|
||||
{-7509, -25259, 32768, 128 << 16},
|
||||
{32768, -29763, -3005, 128 << 16},
|
||||
};
|
||||
|
||||
const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
|
||||
SharpYuvMatrixType matrix_type) {
|
||||
switch (matrix_type) {
|
||||
case kSharpYuvMatrixWebp:
|
||||
return &kWebpMatrix;
|
||||
case kSharpYuvMatrixRec601Limited:
|
||||
return &kRec601LimitedMatrix;
|
||||
case kSharpYuvMatrixRec601Full:
|
||||
return &kRec601FullMatrix;
|
||||
case kSharpYuvMatrixRec709Limited:
|
||||
return &kRec709LimitedMatrix;
|
||||
case kSharpYuvMatrixRec709Full:
|
||||
return &kRec709FullMatrix;
|
||||
case kSharpYuvMatrixNum:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Colorspace utilities.
|
||||
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_CSP_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_CSP_H_
|
||||
|
||||
#include "sharpyuv/sharpyuv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Range of YUV values.
|
||||
typedef enum {
|
||||
kSharpYuvRangeFull, // YUV values between [0;255] (for 8 bit)
|
||||
kSharpYuvRangeLimited // Y in [16;235], YUV in [16;240] (for 8 bit)
|
||||
} SharpYuvRange;
|
||||
|
||||
// Constants that define a YUV color space.
|
||||
typedef struct {
|
||||
// Kr and Kb are defined such that:
|
||||
// Y = Kr * r + Kg * g + Kb * b where Kg = 1 - Kr - Kb.
|
||||
float kr;
|
||||
float kb;
|
||||
int bit_depth; // 8, 10 or 12
|
||||
SharpYuvRange range;
|
||||
} SharpYuvColorSpace;
|
||||
|
||||
// Fills in 'matrix' for the given YUVColorSpace.
|
||||
SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix(
|
||||
const SharpYuvColorSpace* yuv_color_space,
|
||||
SharpYuvConversionMatrix* matrix);
|
||||
|
||||
// Enums for precomputed conversion matrices.
|
||||
typedef enum {
|
||||
kSharpYuvMatrixWebp = 0,
|
||||
kSharpYuvMatrixRec601Limited,
|
||||
kSharpYuvMatrixRec601Full,
|
||||
kSharpYuvMatrixRec709Limited,
|
||||
kSharpYuvMatrixRec709Full,
|
||||
kSharpYuvMatrixNum
|
||||
} SharpYuvMatrixType;
|
||||
|
||||
// Returns a pointer to a matrix for one of the predefined colorspaces.
|
||||
SHARPYUV_EXTERN const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
|
||||
SharpYuvMatrixType matrix_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_SHARPYUV_SHARPYUV_CSP_H_
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions for Sharp YUV.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
static uint16_t clip(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
}
|
||||
|
||||
static uint64_t SharpYuvUpdateY_C(const uint16_t* ref, const uint16_t* src,
|
||||
uint16_t* dst, int len, int bit_depth) {
|
||||
uint64_t diff = 0;
|
||||
int i;
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
for (i = 0; i < len; ++i) {
|
||||
const int diff_y = ref[i] - src[i];
|
||||
const int new_y = (int)dst[i] + diff_y;
|
||||
dst[i] = clip(new_y, max_y);
|
||||
diff += (uint64_t)abs(diff_y);
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void SharpYuvUpdateRGB_C(const int16_t* ref, const int16_t* src,
|
||||
int16_t* dst, int len) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
const int diff_uv = ref[i] - src[i];
|
||||
dst[i] += diff_uv;
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow_C(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth) {
|
||||
int i;
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
for (i = 0; i < len; ++i, ++A, ++B) {
|
||||
const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
|
||||
const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
|
||||
out[2 * i + 0] = clip(best_y[2 * i + 0] + v0, max_y);
|
||||
out[2 * i + 1] = clip(best_y[2 * i + 1] + v1, max_y);
|
||||
}
|
||||
}
|
||||
#endif // !WEBP_NEON_OMIT_C_CODE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
|
||||
uint16_t* dst, int len, int bit_depth);
|
||||
void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref, int16_t* dst,
|
||||
int len);
|
||||
void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth);
|
||||
|
||||
extern VP8CPUInfo SharpYuvGetCPUInfo;
|
||||
extern void InitSharpYuvSSE2(void);
|
||||
extern void InitSharpYuvNEON(void);
|
||||
|
||||
void SharpYuvInitDsp(void) {
|
||||
#if !WEBP_NEON_OMIT_C_CODE
|
||||
SharpYuvUpdateY = SharpYuvUpdateY_C;
|
||||
SharpYuvUpdateRGB = SharpYuvUpdateRGB_C;
|
||||
SharpYuvFilterRow = SharpYuvFilterRow_C;
|
||||
#endif
|
||||
|
||||
if (SharpYuvGetCPUInfo != NULL) {
|
||||
#if defined(WEBP_HAVE_SSE2)
|
||||
if (SharpYuvGetCPUInfo(kSSE2)) {
|
||||
InitSharpYuvSSE2();
|
||||
}
|
||||
#endif // WEBP_HAVE_SSE2
|
||||
}
|
||||
|
||||
#if defined(WEBP_HAVE_NEON)
|
||||
if (WEBP_NEON_OMIT_C_CODE ||
|
||||
(SharpYuvGetCPUInfo != NULL && SharpYuvGetCPUInfo(kNEON))) {
|
||||
InitSharpYuvNEON();
|
||||
}
|
||||
#endif // WEBP_HAVE_NEON
|
||||
|
||||
assert(SharpYuvUpdateY != NULL);
|
||||
assert(SharpYuvUpdateRGB != NULL);
|
||||
assert(SharpYuvFilterRow != NULL);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions for Sharp YUV.
|
||||
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_DSP_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_DSP_H_
|
||||
|
||||
#include "sharpyuv/sharpyuv_cpu.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
extern uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
|
||||
uint16_t* dst, int len, int bit_depth);
|
||||
extern void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref,
|
||||
int16_t* dst, int len);
|
||||
extern void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth);
|
||||
|
||||
void SharpYuvInitDsp(void);
|
||||
|
||||
#endif // WEBP_SHARPYUV_SHARPYUV_DSP_H_
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Gamma correction utilities.
|
||||
|
||||
#include "sharpyuv/sharpyuv_gamma.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "src/webp/types.h"
|
||||
|
||||
// Gamma correction compensates loss of resolution during chroma subsampling.
|
||||
// Size of pre-computed table for converting from gamma to linear.
|
||||
#define GAMMA_TO_LINEAR_TAB_BITS 10
|
||||
#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
|
||||
static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
|
||||
#define LINEAR_TO_GAMMA_TAB_BITS 9
|
||||
#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
|
||||
static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
|
||||
|
||||
static const double kGammaF = 1. / 0.45;
|
||||
#define GAMMA_TO_LINEAR_BITS 16
|
||||
|
||||
static volatile int kGammaTablesSOk = 0;
|
||||
void SharpYuvInitGammaTables(void) {
|
||||
assert(GAMMA_TO_LINEAR_BITS <= 16);
|
||||
if (!kGammaTablesSOk) {
|
||||
int v;
|
||||
const double a = 0.09929682680944;
|
||||
const double thresh = 0.018053968510807;
|
||||
const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
|
||||
// Precompute gamma to linear table.
|
||||
{
|
||||
const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
|
||||
const double a_rec = 1. / (1. + a);
|
||||
for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
|
||||
const double g = norm * v;
|
||||
double value;
|
||||
if (g <= thresh * 4.5) {
|
||||
value = g / 4.5;
|
||||
} else {
|
||||
value = pow(a_rec * (g + a), kGammaF);
|
||||
}
|
||||
kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
|
||||
}
|
||||
// to prevent small rounding errors to cause read-overflow:
|
||||
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
|
||||
kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
|
||||
}
|
||||
// Precompute linear to gamma table.
|
||||
{
|
||||
const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
|
||||
for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
|
||||
const double g = scale * v;
|
||||
double value;
|
||||
if (g <= thresh) {
|
||||
value = 4.5 * g;
|
||||
} else {
|
||||
value = (1. + a) * pow(g, 1. / kGammaF) - a;
|
||||
}
|
||||
kLinearToGammaTabS[v] =
|
||||
(uint32_t)(final_scale * value + 0.5);
|
||||
}
|
||||
// to prevent small rounding errors to cause read-overflow:
|
||||
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
|
||||
kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
|
||||
}
|
||||
kGammaTablesSOk = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE int Shift(int v, int shift) {
|
||||
return (shift >= 0) ? (v << shift) : (v >> -shift);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
|
||||
int tab_pos_shift_right,
|
||||
int tab_value_shift) {
|
||||
const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);
|
||||
// fractional part, in 'tab_pos_shift' fixed-point precision
|
||||
const uint32_t x = v - (tab_pos << tab_pos_shift_right); // fractional part
|
||||
// v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
|
||||
const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);
|
||||
const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);
|
||||
// Final interpolation.
|
||||
const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
|
||||
const int half =
|
||||
(tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;
|
||||
const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
|
||||
const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
|
||||
if (shift > 0) {
|
||||
return kGammaToLinearTabS[v << shift];
|
||||
}
|
||||
return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
|
||||
}
|
||||
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth) {
|
||||
return FixedPointInterpolation(
|
||||
value, kLinearToGammaTabS,
|
||||
(GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
|
||||
bit_depth - GAMMA_TO_LINEAR_BITS);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Gamma correction utilities.
|
||||
|
||||
#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
||||
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Initializes precomputed tables. Must be called once before calling
|
||||
// SharpYuvGammaToLinear or SharpYuvLinearToGamma.
|
||||
void SharpYuvInitGammaTables(void);
|
||||
|
||||
// Converts a gamma color value on 'bit_depth' bits to a 16 bit linear value.
|
||||
uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth);
|
||||
|
||||
// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits.
|
||||
uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions for Sharp YUV.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
static uint16_t clip_NEON(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
}
|
||||
|
||||
static uint64_t SharpYuvUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
|
||||
uint16_t* dst, int len, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
int i;
|
||||
const int16x8_t zero = vdupq_n_s16(0);
|
||||
const int16x8_t max = vdupq_n_s16(max_y);
|
||||
uint64x2_t sum = vdupq_n_u64(0);
|
||||
uint64_t diff;
|
||||
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
|
||||
const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
|
||||
const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_y
|
||||
const int16x8_t F = vaddq_s16(C, D); // new_y
|
||||
const uint16x8_t H =
|
||||
vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
|
||||
const int16x8_t I = vabsq_s16(D); // abs(diff_y)
|
||||
vst1q_u16(dst + i, H);
|
||||
sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
|
||||
}
|
||||
diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
|
||||
for (; i < len; ++i) {
|
||||
const int diff_y = ref[i] - src[i];
|
||||
const int new_y = (int)(dst[i]) + diff_y;
|
||||
dst[i] = clip_NEON(new_y, max_y);
|
||||
diff += (uint64_t)(abs(diff_y));
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void SharpYuvUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
|
||||
int16_t* dst, int len) {
|
||||
int i;
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const int16x8_t A = vld1q_s16(ref + i);
|
||||
const int16x8_t B = vld1q_s16(src + i);
|
||||
const int16x8_t C = vld1q_s16(dst + i);
|
||||
const int16x8_t D = vsubq_s16(A, B); // diff_uv
|
||||
const int16x8_t E = vaddq_s16(C, D); // new_uv
|
||||
vst1q_s16(dst + i, E);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
const int diff_uv = ref[i] - src[i];
|
||||
dst[i] += diff_uv;
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow16_NEON(const int16_t* A, const int16_t* B,
|
||||
int len, const uint16_t* best_y,
|
||||
uint16_t* out, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
int i;
|
||||
const int16x8_t max = vdupq_n_s16(max_y);
|
||||
const int16x8_t zero = vdupq_n_s16(0);
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const int16x8_t a0 = vld1q_s16(A + i + 0);
|
||||
const int16x8_t a1 = vld1q_s16(A + i + 1);
|
||||
const int16x8_t b0 = vld1q_s16(B + i + 0);
|
||||
const int16x8_t b1 = vld1q_s16(B + i + 1);
|
||||
const int16x8_t a0b1 = vaddq_s16(a0, b1);
|
||||
const int16x8_t a1b0 = vaddq_s16(a1, b0);
|
||||
const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0); // A0+A1+B0+B1
|
||||
const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1); // 2*(A0+B1)
|
||||
const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0); // 2*(A1+B0)
|
||||
const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
|
||||
const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
|
||||
const int16x8_t e0 = vrhaddq_s16(c1, a0);
|
||||
const int16x8_t e1 = vrhaddq_s16(c0, a1);
|
||||
const int16x8x2_t f = vzipq_s16(e0, e1);
|
||||
const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
|
||||
const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
|
||||
const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
|
||||
const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
|
||||
const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
|
||||
const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
|
||||
vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
|
||||
vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
const int a0b1 = A[i + 0] + B[i + 1];
|
||||
const int a1b0 = A[i + 1] + B[i + 0];
|
||||
const int a0a1b0b1 = a0b1 + a1b0 + 8;
|
||||
const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
|
||||
const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
|
||||
out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
|
||||
out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow32_NEON(const int16_t* A, const int16_t* B,
|
||||
int len, const uint16_t* best_y,
|
||||
uint16_t* out, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
int i;
|
||||
const uint16x8_t max = vdupq_n_u16(max_y);
|
||||
for (i = 0; i + 4 <= len; i += 4) {
|
||||
const int16x4_t a0 = vld1_s16(A + i + 0);
|
||||
const int16x4_t a1 = vld1_s16(A + i + 1);
|
||||
const int16x4_t b0 = vld1_s16(B + i + 0);
|
||||
const int16x4_t b1 = vld1_s16(B + i + 1);
|
||||
const int32x4_t a0b1 = vaddl_s16(a0, b1);
|
||||
const int32x4_t a1b0 = vaddl_s16(a1, b0);
|
||||
const int32x4_t a0a1b0b1 = vaddq_s32(a0b1, a1b0); // A0+A1+B0+B1
|
||||
const int32x4_t a0b1_2 = vaddq_s32(a0b1, a0b1); // 2*(A0+B1)
|
||||
const int32x4_t a1b0_2 = vaddq_s32(a1b0, a1b0); // 2*(A1+B0)
|
||||
const int32x4_t c0 = vshrq_n_s32(vaddq_s32(a0b1_2, a0a1b0b1), 3);
|
||||
const int32x4_t c1 = vshrq_n_s32(vaddq_s32(a1b0_2, a0a1b0b1), 3);
|
||||
const int32x4_t e0 = vrhaddq_s32(c1, vmovl_s16(a0));
|
||||
const int32x4_t e1 = vrhaddq_s32(c0, vmovl_s16(a1));
|
||||
const int32x4x2_t f = vzipq_s32(e0, e1);
|
||||
|
||||
const int16x8_t g = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i));
|
||||
const int32x4_t h0 = vaddw_s16(f.val[0], vget_low_s16(g));
|
||||
const int32x4_t h1 = vaddw_s16(f.val[1], vget_high_s16(g));
|
||||
const uint16x8_t i_16 = vcombine_u16(vqmovun_s32(h0), vqmovun_s32(h1));
|
||||
const uint16x8_t i_clamped = vminq_u16(i_16, max);
|
||||
vst1q_u16(out + 2 * i + 0, i_clamped);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
const int a0b1 = A[i + 0] + B[i + 1];
|
||||
const int a1b0 = A[i + 1] + B[i + 0];
|
||||
const int a0a1b0b1 = a0b1 + a1b0 + 8;
|
||||
const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
|
||||
const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
|
||||
out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
|
||||
out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth) {
|
||||
if (bit_depth <= 10) {
|
||||
SharpYuvFilterRow16_NEON(A, B, len, best_y, out, bit_depth);
|
||||
} else {
|
||||
SharpYuvFilterRow32_NEON(A, B, len, best_y, out, bit_depth);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
extern void InitSharpYuvNEON(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvNEON(void) {
|
||||
SharpYuvUpdateY = SharpYuvUpdateY_NEON;
|
||||
SharpYuvUpdateRGB = SharpYuvUpdateRGB_NEON;
|
||||
SharpYuvFilterRow = SharpYuvFilterRow_NEON;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_NEON
|
||||
|
||||
extern void InitSharpYuvNEON(void);
|
||||
|
||||
void InitSharpYuvNEON(void) {}
|
||||
|
||||
#endif // WEBP_USE_NEON
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2022 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions for Sharp YUV.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "sharpyuv/sharpyuv_dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
#include <stdlib.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
static uint16_t clip_SSE2(int v, int max) {
|
||||
return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
|
||||
}
|
||||
|
||||
static uint64_t SharpYuvUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
|
||||
uint16_t* dst, int len, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
uint64_t diff = 0;
|
||||
uint32_t tmp[4];
|
||||
int i;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i max = _mm_set1_epi16(max_y);
|
||||
const __m128i one = _mm_set1_epi16(1);
|
||||
__m128i sum = zero;
|
||||
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
|
||||
const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
|
||||
const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
|
||||
const __m128i D = _mm_sub_epi16(A, B); // diff_y
|
||||
const __m128i E = _mm_cmpgt_epi16(zero, D); // sign (-1 or 0)
|
||||
const __m128i F = _mm_add_epi16(C, D); // new_y
|
||||
const __m128i G = _mm_or_si128(E, one); // -1 or 1
|
||||
const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
|
||||
const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
|
||||
_mm_storeu_si128((__m128i*)(dst + i), H);
|
||||
sum = _mm_add_epi32(sum, I);
|
||||
}
|
||||
_mm_storeu_si128((__m128i*)tmp, sum);
|
||||
diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
|
||||
for (; i < len; ++i) {
|
||||
const int diff_y = ref[i] - src[i];
|
||||
const int new_y = (int)dst[i] + diff_y;
|
||||
dst[i] = clip_SSE2(new_y, max_y);
|
||||
diff += (uint64_t)abs(diff_y);
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void SharpYuvUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
|
||||
int16_t* dst, int len) {
|
||||
int i = 0;
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
|
||||
const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
|
||||
const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
|
||||
const __m128i D = _mm_sub_epi16(A, B); // diff_uv
|
||||
const __m128i E = _mm_add_epi16(C, D); // new_uv
|
||||
_mm_storeu_si128((__m128i*)(dst + i), E);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
const int diff_uv = ref[i] - src[i];
|
||||
dst[i] += diff_uv;
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow16_SSE2(const int16_t* A, const int16_t* B,
|
||||
int len, const uint16_t* best_y,
|
||||
uint16_t* out, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
int i;
|
||||
const __m128i kCst8 = _mm_set1_epi16(8);
|
||||
const __m128i max = _mm_set1_epi16(max_y);
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
for (i = 0; i + 8 <= len; i += 8) {
|
||||
const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
|
||||
const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
|
||||
const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
|
||||
const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
|
||||
const __m128i a0b1 = _mm_add_epi16(a0, b1);
|
||||
const __m128i a1b0 = _mm_add_epi16(a1, b0);
|
||||
const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1
|
||||
const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
|
||||
const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
|
||||
const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
|
||||
const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
|
||||
const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
|
||||
const __m128i d0 = _mm_add_epi16(c1, a0);
|
||||
const __m128i d1 = _mm_add_epi16(c0, a1);
|
||||
const __m128i e0 = _mm_srai_epi16(d0, 1);
|
||||
const __m128i e1 = _mm_srai_epi16(d1, 1);
|
||||
const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
|
||||
const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
|
||||
const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
|
||||
const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
|
||||
const __m128i h0 = _mm_add_epi16(g0, f0);
|
||||
const __m128i h1 = _mm_add_epi16(g1, f1);
|
||||
const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
|
||||
const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
|
||||
_mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
|
||||
_mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
// (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
|
||||
// = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
|
||||
// We reuse the common sub-expressions.
|
||||
const int a0b1 = A[i + 0] + B[i + 1];
|
||||
const int a1b0 = A[i + 1] + B[i + 0];
|
||||
const int a0a1b0b1 = a0b1 + a1b0 + 8;
|
||||
const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
|
||||
const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
|
||||
out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
|
||||
out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE __m128i s16_to_s32(__m128i in) {
|
||||
return _mm_srai_epi32(_mm_unpacklo_epi16(in, in), 16);
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow32_SSE2(const int16_t* A, const int16_t* B,
|
||||
int len, const uint16_t* best_y,
|
||||
uint16_t* out, int bit_depth) {
|
||||
const int max_y = (1 << bit_depth) - 1;
|
||||
int i;
|
||||
const __m128i kCst8 = _mm_set1_epi32(8);
|
||||
const __m128i max = _mm_set1_epi16(max_y);
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
for (i = 0; i + 4 <= len; i += 4) {
|
||||
const __m128i a0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 0)));
|
||||
const __m128i a1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 1)));
|
||||
const __m128i b0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 0)));
|
||||
const __m128i b1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 1)));
|
||||
const __m128i a0b1 = _mm_add_epi32(a0, b1);
|
||||
const __m128i a1b0 = _mm_add_epi32(a1, b0);
|
||||
const __m128i a0a1b0b1 = _mm_add_epi32(a0b1, a1b0); // A0+A1+B0+B1
|
||||
const __m128i a0a1b0b1_8 = _mm_add_epi32(a0a1b0b1, kCst8);
|
||||
const __m128i a0b1_2 = _mm_add_epi32(a0b1, a0b1); // 2*(A0+B1)
|
||||
const __m128i a1b0_2 = _mm_add_epi32(a1b0, a1b0); // 2*(A1+B0)
|
||||
const __m128i c0 = _mm_srai_epi32(_mm_add_epi32(a0b1_2, a0a1b0b1_8), 3);
|
||||
const __m128i c1 = _mm_srai_epi32(_mm_add_epi32(a1b0_2, a0a1b0b1_8), 3);
|
||||
const __m128i d0 = _mm_add_epi32(c1, a0);
|
||||
const __m128i d1 = _mm_add_epi32(c0, a1);
|
||||
const __m128i e0 = _mm_srai_epi32(d0, 1);
|
||||
const __m128i e1 = _mm_srai_epi32(d1, 1);
|
||||
const __m128i f0 = _mm_unpacklo_epi32(e0, e1);
|
||||
const __m128i f1 = _mm_unpackhi_epi32(e0, e1);
|
||||
const __m128i g = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
|
||||
const __m128i h_16 = _mm_add_epi16(g, _mm_packs_epi32(f0, f1));
|
||||
const __m128i final = _mm_max_epi16(_mm_min_epi16(h_16, max), zero);
|
||||
_mm_storeu_si128((__m128i*)(out + 2 * i + 0), final);
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
// (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
|
||||
// = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
|
||||
// We reuse the common sub-expressions.
|
||||
const int a0b1 = A[i + 0] + B[i + 1];
|
||||
const int a1b0 = A[i + 1] + B[i + 0];
|
||||
const int a0a1b0b1 = a0b1 + a1b0 + 8;
|
||||
const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
|
||||
const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
|
||||
out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
|
||||
out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
|
||||
}
|
||||
}
|
||||
|
||||
static void SharpYuvFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
|
||||
const uint16_t* best_y, uint16_t* out,
|
||||
int bit_depth) {
|
||||
if (bit_depth <= 10) {
|
||||
SharpYuvFilterRow16_SSE2(A, B, len, best_y, out, bit_depth);
|
||||
} else {
|
||||
SharpYuvFilterRow32_SSE2(A, B, len, best_y, out, bit_depth);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
extern void InitSharpYuvSSE2(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvSSE2(void) {
|
||||
SharpYuvUpdateY = SharpYuvUpdateY_SSE2;
|
||||
SharpYuvUpdateRGB = SharpYuvUpdateRGB_SSE2;
|
||||
SharpYuvFilterRow = SharpYuvFilterRow_SSE2;
|
||||
}
|
||||
#else // !WEBP_USE_SSE2
|
||||
|
||||
extern void InitSharpYuvSSE2(void);
|
||||
|
||||
void InitSharpYuvSSE2(void) {}
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
|
@ -0,0 +1,55 @@
|
|||
# The mux and demux libraries depend on libwebp, thus the '.' to force
|
||||
# the build order so it's available to them.
|
||||
SUBDIRS = dec enc dsp utils .
|
||||
if BUILD_MUX
|
||||
SUBDIRS += mux
|
||||
endif
|
||||
if BUILD_DEMUX
|
||||
SUBDIRS += demux
|
||||
endif
|
||||
|
||||
lib_LTLIBRARIES = libwebp.la
|
||||
|
||||
if BUILD_LIBWEBPDECODER
|
||||
lib_LTLIBRARIES += libwebpdecoder.la
|
||||
endif
|
||||
|
||||
common_HEADERS =
|
||||
common_HEADERS += webp/decode.h
|
||||
common_HEADERS += webp/types.h
|
||||
commondir = $(includedir)/webp
|
||||
|
||||
libwebp_la_SOURCES =
|
||||
libwebpinclude_HEADERS =
|
||||
libwebpinclude_HEADERS += webp/encode.h
|
||||
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += webp/format_constants.h
|
||||
|
||||
libwebp_la_LIBADD =
|
||||
libwebp_la_LIBADD += dec/libwebpdecode.la
|
||||
libwebp_la_LIBADD += dsp/libwebpdsp.la
|
||||
libwebp_la_LIBADD += enc/libwebpencode.la
|
||||
libwebp_la_LIBADD += utils/libwebputils.la
|
||||
|
||||
# Use '-no-undefined' to declare that libwebp does not depend on any libraries
|
||||
# other than the ones listed on the command line, i.e., after linking, it will
|
||||
# not have unresolved symbols. Some platforms (Windows among them) require all
|
||||
# symbols in shared libraries to be resolved at library creation.
|
||||
libwebp_la_LDFLAGS = -no-undefined -version-info 8:8:1
|
||||
libwebpincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebp.pc
|
||||
|
||||
if BUILD_LIBWEBPDECODER
|
||||
libwebpdecoder_la_SOURCES =
|
||||
|
||||
libwebpdecoder_la_LIBADD =
|
||||
libwebpdecoder_la_LIBADD += dec/libwebpdecode.la
|
||||
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
|
||||
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
|
||||
|
||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:8:1
|
||||
pkgconfig_DATA += libwebpdecoder.pc
|
||||
endif
|
||||
|
||||
${pkgconfig_DATA}: ${top_builddir}/config.status
|
|
@ -0,0 +1,29 @@
|
|||
AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
|
||||
noinst_LTLIBRARIES = libwebpdecode.la
|
||||
|
||||
libwebpdecode_la_SOURCES =
|
||||
libwebpdecode_la_SOURCES += alpha_dec.c
|
||||
libwebpdecode_la_SOURCES += alphai_dec.h
|
||||
libwebpdecode_la_SOURCES += buffer_dec.c
|
||||
libwebpdecode_la_SOURCES += common_dec.h
|
||||
libwebpdecode_la_SOURCES += vp8_dec.h
|
||||
libwebpdecode_la_SOURCES += frame_dec.c
|
||||
libwebpdecode_la_SOURCES += idec_dec.c
|
||||
libwebpdecode_la_SOURCES += io_dec.c
|
||||
libwebpdecode_la_SOURCES += quant_dec.c
|
||||
libwebpdecode_la_SOURCES += tree_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8i_dec.h
|
||||
libwebpdecode_la_SOURCES += vp8l_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8li_dec.h
|
||||
libwebpdecode_la_SOURCES += webp_dec.c
|
||||
libwebpdecode_la_SOURCES += webpi_dec.h
|
||||
|
||||
libwebpdecodeinclude_HEADERS =
|
||||
libwebpdecodeinclude_HEADERS += ../webp/decode.h
|
||||
libwebpdecodeinclude_HEADERS += ../webp/types.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += ../webp/format_constants.h
|
||||
|
||||
libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libwebpdecodeincludedir = $(includedir)/webp
|
|
@ -0,0 +1,232 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha-plane decompression.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/utils/quant_levels_dec_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/webp/format_constants.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ALPHDecoder object.
|
||||
|
||||
// Allocates a new alpha decoder instance.
|
||||
static ALPHDecoder* ALPHNew(void) {
|
||||
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
return dec;
|
||||
}
|
||||
|
||||
// Clears and deallocates an alpha decoder instance.
|
||||
static void ALPHDelete(ALPHDecoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8LDelete(dec->vp8l_dec_);
|
||||
dec->vp8l_dec_ = NULL;
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decoding.
|
||||
|
||||
// Initialize alpha decoding by parsing the alpha header and decoding the image
|
||||
// header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error in alpha header (data too short, invalid
|
||||
// compression method or filter, error in lossless header data etc).
|
||||
static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
size_t data_size, const VP8Io* const src_io,
|
||||
uint8_t* output) {
|
||||
int ok = 0;
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
int rsrv;
|
||||
VP8Io* const io = &dec->io_;
|
||||
|
||||
assert(data != NULL && output != NULL && src_io != NULL);
|
||||
|
||||
VP8FiltersInit();
|
||||
dec->output_ = output;
|
||||
dec->width_ = src_io->width;
|
||||
dec->height_ = src_io->height;
|
||||
assert(dec->width_ > 0 && dec->height_ > 0);
|
||||
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dec->method_ = (data[0] >> 0) & 0x03;
|
||||
dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
|
||||
dec->pre_processing_ = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (dec->method_ < ALPHA_NO_COMPRESSION ||
|
||||
dec->method_ > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
dec->filter_ >= WEBP_FILTER_LAST ||
|
||||
dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the necessary parameters from src_io to io
|
||||
VP8InitIo(io);
|
||||
WebPInitCustomIo(NULL, io);
|
||||
io->opaque = dec;
|
||||
io->width = src_io->width;
|
||||
io->height = src_io->height;
|
||||
|
||||
io->use_cropping = src_io->use_cropping;
|
||||
io->crop_left = src_io->crop_left;
|
||||
io->crop_right = src_io->crop_right;
|
||||
io->crop_top = src_io->crop_top;
|
||||
io->crop_bottom = src_io->crop_bottom;
|
||||
// No need to copy the scaling parameters.
|
||||
|
||||
if (dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width_ * dec->height_;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
||||
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
|
||||
// starting from row number 'row'. It assumes that rows up to (row - 1) have
|
||||
// already been decoded.
|
||||
// Returns false in case of bitstream error.
|
||||
static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
const int width = alph_dec->width_;
|
||||
const int height = alph_dec->io_.crop_bottom;
|
||||
if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
int y;
|
||||
const uint8_t* prev_line = dec->alpha_prev_line_;
|
||||
const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
|
||||
uint8_t* dst = dec->alpha_plane_ + row * width;
|
||||
assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
|
||||
if (alph_dec->filter_ != WEBP_FILTER_NONE) {
|
||||
assert(WebPUnfilters[alph_dec->filter_] != NULL);
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < num_rows; ++y) {
|
||||
memcpy(dst, deltas, width * sizeof(*dst));
|
||||
prev_line = dst;
|
||||
dst += width;
|
||||
deltas += width;
|
||||
}
|
||||
}
|
||||
dec->alpha_prev_line_ = prev_line;
|
||||
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
|
||||
assert(alph_dec->vp8l_dec_ != NULL);
|
||||
if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (row + num_rows >= height) {
|
||||
dec->is_alpha_decoded_ = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
|
||||
const int stride = io->width;
|
||||
const int height = io->crop_bottom;
|
||||
const uint64_t alpha_size = (uint64_t)stride * height;
|
||||
assert(dec->alpha_plane_mem_ == NULL);
|
||||
dec->alpha_plane_mem_ =
|
||||
(uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
|
||||
if (dec->alpha_plane_mem_ == NULL) {
|
||||
return 0;
|
||||
}
|
||||
dec->alpha_plane_ = dec->alpha_plane_mem_;
|
||||
dec->alpha_prev_line_ = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
|
||||
assert(dec != NULL);
|
||||
WebPSafeFree(dec->alpha_plane_mem_);
|
||||
dec->alpha_plane_mem_ = NULL;
|
||||
dec->alpha_plane_ = NULL;
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main entry point.
|
||||
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows) {
|
||||
const int width = io->width;
|
||||
const int height = io->crop_bottom;
|
||||
|
||||
assert(dec != NULL && io != NULL);
|
||||
|
||||
if (row < 0 || num_rows <= 0 || row + num_rows > height) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dec->is_alpha_decoded_) {
|
||||
if (dec->alph_dec_ == NULL) { // Initialize decoder.
|
||||
dec->alph_dec_ = ALPHNew();
|
||||
if (dec->alph_dec_ == NULL) return NULL;
|
||||
if (!AllocateAlphaPlane(dec, io)) goto Error;
|
||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
||||
io, dec->alpha_plane_)) {
|
||||
goto Error;
|
||||
}
|
||||
// if we allowed use of alpha dithering, check whether it's needed at all
|
||||
if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) {
|
||||
dec->alpha_dithering_ = 0; // disable dithering
|
||||
} else {
|
||||
num_rows = height - row; // decode everything in one pass
|
||||
}
|
||||
}
|
||||
|
||||
assert(dec->alph_dec_ != NULL);
|
||||
assert(row + num_rows <= height);
|
||||
if (!ALPHDecode(dec, row, num_rows)) goto Error;
|
||||
|
||||
if (dec->is_alpha_decoded_) { // finished?
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
if (dec->alpha_dithering_ > 0) {
|
||||
uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width
|
||||
+ io->crop_left;
|
||||
if (!WebPDequantizeLevels(alpha,
|
||||
io->crop_right - io->crop_left,
|
||||
io->crop_bottom - io->crop_top,
|
||||
width, dec->alpha_dithering_)) {
|
||||
goto Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * width;
|
||||
|
||||
Error:
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha decoder: internal header.
|
||||
//
|
||||
// Author: Urvang (urvang@google.com)
|
||||
|
||||
#ifndef WEBP_DEC_ALPHAI_DEC_H_
|
||||
#define WEBP_DEC_ALPHAI_DEC_H_
|
||||
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/filters_utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct VP8LDecoder; // Defined in dec/vp8li.h.
|
||||
|
||||
typedef struct ALPHDecoder ALPHDecoder;
|
||||
struct ALPHDecoder {
|
||||
int width_;
|
||||
int height_;
|
||||
int method_;
|
||||
WEBP_FILTER_TYPE filter_;
|
||||
int pre_processing_;
|
||||
struct VP8LDecoder* vp8l_dec_;
|
||||
VP8Io io_;
|
||||
int use_8b_decode_; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output_;
|
||||
const uint8_t* prev_line_; // last output row (or NULL)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// Deallocate memory associated to dec->alpha_plane_ decoding
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_DEC_ALPHAI_DEC_H_
|
|
@ -0,0 +1,310 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Everything about WebPDecBuffer
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
// Number of bytes per pixel for the different color-spaces.
|
||||
static const uint8_t kModeBpp[MODE_LAST] = {
|
||||
3, 4, 3, 4, 4, 2, 2,
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1 };
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
// Convert to an integer to handle both the unsigned/signed enum cases
|
||||
// without the need for casting to remove type limit warnings.
|
||||
static int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
// strictly speaking, the very last (or first, if flipped) row
|
||||
// doesn't require padding.
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||
const int width = buffer->width;
|
||||
const int height = buffer->height;
|
||||
if (!IsValidColorspace(mode)) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int y_stride = abs(buf->y_stride);
|
||||
const int u_stride = abs(buf->u_stride);
|
||||
const int v_stride = abs(buf->v_stride);
|
||||
const int a_stride = abs(buf->a_stride);
|
||||
const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
|
||||
const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
|
||||
const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
|
||||
const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
|
||||
ok &= (y_size <= buf->y_size);
|
||||
ok &= (u_size <= buf->u_size);
|
||||
ok &= (v_size <= buf->v_size);
|
||||
ok &= (y_stride >= width);
|
||||
ok &= (u_stride >= uv_width);
|
||||
ok &= (v_stride >= uv_width);
|
||||
ok &= (buf->y != NULL);
|
||||
ok &= (buf->u != NULL);
|
||||
ok &= (buf->v != NULL);
|
||||
if (mode == MODE_YUVA) {
|
||||
ok &= (a_stride >= width);
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size =
|
||||
MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride);
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
}
|
||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
#undef MIN_BUFFER_SIZE
|
||||
|
||||
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
const int w = buffer->width;
|
||||
const int h = buffer->height;
|
||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||
|
||||
if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) {
|
||||
uint8_t* output;
|
||||
int uv_stride = 0, a_stride = 0;
|
||||
uint64_t uv_size = 0, a_size = 0, total_size;
|
||||
// We need memory and it hasn't been allocated yet.
|
||||
// => initialize output buffer, now that dimensions are known.
|
||||
int stride;
|
||||
uint64_t size;
|
||||
|
||||
if ((uint64_t)w * kModeBpp[mode] >= (1ull << 31)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
stride = w * kModeBpp[mode];
|
||||
size = (uint64_t)stride * h;
|
||||
if (!WebPIsRGBMode(mode)) {
|
||||
uv_stride = (w + 1) / 2;
|
||||
uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
|
||||
if (mode == MODE_YUVA) {
|
||||
a_stride = w;
|
||||
a_size = (uint64_t)a_stride * h;
|
||||
}
|
||||
}
|
||||
total_size = size + 2 * uv_size + a_size;
|
||||
|
||||
output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
|
||||
if (output == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
buffer->private_memory = output;
|
||||
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
buf->y = output;
|
||||
buf->y_stride = stride;
|
||||
buf->y_size = (size_t)size;
|
||||
buf->u = output + size;
|
||||
buf->u_stride = uv_stride;
|
||||
buf->u_size = (size_t)uv_size;
|
||||
buf->v = output + size + uv_size;
|
||||
buf->v_stride = uv_stride;
|
||||
buf->v_size = (size_t)uv_size;
|
||||
if (mode == MODE_YUVA) {
|
||||
buf->a = output + size + 2 * uv_size;
|
||||
}
|
||||
buf->a_size = (size_t)a_size;
|
||||
buf->a_stride = a_stride;
|
||||
} else { // RGBA initialization
|
||||
WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
buf->rgba = output;
|
||||
buf->stride = stride;
|
||||
buf->size = (size_t)size;
|
||||
}
|
||||
}
|
||||
return CheckDecBuffer(buffer);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
|
||||
if (buffer == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (WebPIsRGBMode(buffer->colorspace)) {
|
||||
WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
buf->rgba += (int64_t)(buffer->height - 1) * buf->stride;
|
||||
buf->stride = -buf->stride;
|
||||
} else {
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int64_t H = buffer->height;
|
||||
buf->y += (H - 1) * buf->y_stride;
|
||||
buf->y_stride = -buf->y_stride;
|
||||
buf->u += ((H - 1) >> 1) * buf->u_stride;
|
||||
buf->u_stride = -buf->u_stride;
|
||||
buf->v += ((H - 1) >> 1) * buf->v_stride;
|
||||
buf->v_stride = -buf->v_stride;
|
||||
if (buf->a != NULL) {
|
||||
buf->a += (H - 1) * buf->a_stride;
|
||||
buf->a_stride = -buf->a_stride;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const buffer) {
|
||||
VP8StatusCode status;
|
||||
if (buffer == NULL || width <= 0 || height <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options->use_cropping) {
|
||||
const int cw = options->crop_width;
|
||||
const int ch = options->crop_height;
|
||||
const int x = options->crop_left & ~1;
|
||||
const int y = options->crop_top & ~1;
|
||||
if (!WebPCheckCropDimensions(width, height, x, y, cw, ch)) {
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
}
|
||||
width = cw;
|
||||
height = ch;
|
||||
}
|
||||
|
||||
if (options->use_scaling) {
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (!WebPRescalerGetScaledDimensions(
|
||||
width, height, &scaled_width, &scaled_height)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
width = scaled_width;
|
||||
height = scaled_height;
|
||||
#else
|
||||
return VP8_STATUS_INVALID_PARAM; // rescaling not supported
|
||||
#endif
|
||||
}
|
||||
}
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
|
||||
// Then, allocate buffer for real.
|
||||
status = AllocateBuffer(buffer);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
|
||||
// Use the stride trick if vertical flip is needed.
|
||||
if (options != NULL && options->flip) {
|
||||
status = WebPFlipBuffer(buffer);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// constructors / destructors
|
||||
|
||||
int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return 0; // version mismatch
|
||||
}
|
||||
if (buffer == NULL) return 0;
|
||||
memset(buffer, 0, sizeof(*buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
||||
if (buffer != NULL) {
|
||||
if (buffer->is_external_memory <= 0) {
|
||||
WebPSafeFree(buffer->private_memory);
|
||||
}
|
||||
buffer->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf,
|
||||
WebPDecBuffer* const dst_buf) {
|
||||
assert(src_buf != NULL && dst_buf != NULL);
|
||||
assert(src_buf->colorspace == dst_buf->colorspace);
|
||||
|
||||
dst_buf->width = src_buf->width;
|
||||
dst_buf->height = src_buf->height;
|
||||
if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (WebPIsRGBMode(src_buf->colorspace)) {
|
||||
const WebPRGBABuffer* const src = &src_buf->u.RGBA;
|
||||
const WebPRGBABuffer* const dst = &dst_buf->u.RGBA;
|
||||
WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride,
|
||||
src_buf->width * kModeBpp[src_buf->colorspace],
|
||||
src_buf->height);
|
||||
} else {
|
||||
const WebPYUVABuffer* const src = &src_buf->u.YUVA;
|
||||
const WebPYUVABuffer* const dst = &dst_buf->u.YUVA;
|
||||
WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride,
|
||||
src_buf->width, src_buf->height);
|
||||
WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride,
|
||||
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
|
||||
WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride,
|
||||
(src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
|
||||
if (WebPIsAlphaMode(src_buf->colorspace)) {
|
||||
WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride,
|
||||
src_buf->width, src_buf->height);
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
|
||||
const WebPBitstreamFeatures* const features) {
|
||||
assert(output != NULL);
|
||||
return (output->is_external_memory >= 2) &&
|
||||
WebPIsPremultipliedMode(output->colorspace) &&
|
||||
(features != NULL && features->has_alpha);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Definitions and macros common to encoding and decoding
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_DEC_COMMON_DEC_H_
|
||||
#define WEBP_DEC_COMMON_DEC_H_
|
||||
|
||||
// intra prediction modes
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
NUM_PRED_MODES = 4,
|
||||
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7 };
|
||||
|
||||
enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11
|
||||
};
|
||||
|
||||
#endif // WEBP_DEC_COMMON_DEC_H_
|
|
@ -0,0 +1,803 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Frame-reconstruction function. Memory allocation.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
static const uint16_t kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
|
||||
static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
if (mb_x == 0) {
|
||||
return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
|
||||
} else {
|
||||
return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
|
||||
memcpy(dst, src, 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
switch (bits >> 30) {
|
||||
case 3:
|
||||
VP8Transform(src, dst, 0);
|
||||
break;
|
||||
case 2:
|
||||
VP8TransformAC3(src, dst);
|
||||
break;
|
||||
case 1:
|
||||
VP8TransformDC(src, dst);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoUVTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
} else {
|
||||
VP8TransformDCUV(src, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ReconstructRow(const VP8Decoder* const dec,
|
||||
const VP8ThreadContext* ctx) {
|
||||
int j;
|
||||
int mb_x;
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int cache_id = ctx->id_;
|
||||
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
|
||||
|
||||
// Initialize left-most block.
|
||||
for (j = 0; j < 16; ++j) {
|
||||
y_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
u_dst[j * BPS - 1] = 129;
|
||||
v_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
|
||||
// Init top-left sample on left column too.
|
||||
if (mb_y > 0) {
|
||||
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
|
||||
} else {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
memset(u_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// Reconstruct one row.
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
const VP8MBData* const block = ctx->mb_data_ + mb_x;
|
||||
|
||||
// Rotate in the left samples from previously decoded block. We move four
|
||||
// pixels at a time for alignment reason, and because of in-loop filter.
|
||||
if (mb_x > 0) {
|
||||
for (j = -1; j < 16; ++j) {
|
||||
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
|
||||
}
|
||||
for (j = -1; j < 8; ++j) {
|
||||
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
|
||||
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
|
||||
}
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
|
||||
const int16_t* const coeffs = block->coeffs_;
|
||||
uint32_t bits = block->non_zero_y_;
|
||||
int n;
|
||||
|
||||
if (mb_y > 0) {
|
||||
memcpy(y_dst - BPS, top_yuv[0].y, 16);
|
||||
memcpy(u_dst - BPS, top_yuv[0].u, 8);
|
||||
memcpy(v_dst - BPS, top_yuv[0].v, 8);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
if (block->is_i4x4_) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (mb_y > 0) {
|
||||
if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
|
||||
memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
} else {
|
||||
memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
|
||||
|
||||
// predict and add residuals for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[block->imodes_[n]](dst);
|
||||
DoTransform(bits, coeffs + n * 16, dst);
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (bits != 0) {
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const uint32_t bits_uv = block->non_zero_uv_;
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
|
||||
DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst);
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (mb_y < dec->mb_h_ - 1) {
|
||||
memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16);
|
||||
memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8);
|
||||
memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
// Transfer reconstructed samples from yuv_b_ cache to final destination.
|
||||
{
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
|
||||
uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
|
||||
uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
|
||||
memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Filtering
|
||||
|
||||
// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
|
||||
// for caching, given a filtering level.
|
||||
// Simple filter: up to 2 luma samples are read and 1 is written.
|
||||
// Complex filter: up to 4 luma samples are read and 3 are written. Same for
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||
|
||||
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int y_bps = dec->cache_y_stride_;
|
||||
const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
|
||||
const int ilevel = f_info->f_ilevel_;
|
||||
const int limit = f_info->f_limit_;
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
assert(limit >= 3);
|
||||
if (dec->filter_type_ == 1) { // simple
|
||||
if (mb_x > 0) {
|
||||
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8SimpleHFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
if (mb_y > 0) {
|
||||
VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8SimpleVFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh_;
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
if (mb_y > 0) {
|
||||
VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the decoded macroblock row (if needed)
|
||||
static void FilterRow(const VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
const int mb_y = dec->thread_ctx_.mb_y_;
|
||||
assert(dec->thread_ctx_.filter_row_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
DoFilter(dec, mb_x, mb_y);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
|
||||
|
||||
static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
if (dec->filter_type_ > 0) {
|
||||
int s;
|
||||
const VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
int i4x4;
|
||||
// First, compute the initial level
|
||||
int base_level;
|
||||
if (dec->segment_hdr_.use_segment_) {
|
||||
base_level = dec->segment_hdr_.filter_strength_[s];
|
||||
if (!dec->segment_hdr_.absolute_delta_) {
|
||||
base_level += hdr->level_;
|
||||
}
|
||||
} else {
|
||||
base_level = hdr->level_;
|
||||
}
|
||||
for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
|
||||
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
||||
int level = base_level;
|
||||
if (hdr->use_lf_delta_) {
|
||||
level += hdr->ref_lf_delta_[0];
|
||||
if (i4x4) {
|
||||
level += hdr->mode_lf_delta_[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
if (level > 0) {
|
||||
int ilevel = level;
|
||||
if (hdr->sharpness_ > 0) {
|
||||
if (hdr->sharpness_ > 4) {
|
||||
ilevel >>= 2;
|
||||
} else {
|
||||
ilevel >>= 1;
|
||||
}
|
||||
if (ilevel > 9 - hdr->sharpness_) {
|
||||
ilevel = 9 - hdr->sharpness_;
|
||||
}
|
||||
}
|
||||
if (ilevel < 1) ilevel = 1;
|
||||
info->f_ilevel_ = ilevel;
|
||||
info->f_limit_ = 2 * level + ilevel;
|
||||
info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
info->f_limit_ = 0; // no filtering
|
||||
}
|
||||
info->f_inner_ = i4x4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dithering
|
||||
|
||||
// minimal amp that will provide a non-zero dithering effect
|
||||
#define MIN_DITHER_AMP 4
|
||||
|
||||
#define DITHER_AMP_TAB_SIZE 12
|
||||
static const uint8_t kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
||||
// roughly, it's dqm->uv_mat_[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec) {
|
||||
assert(dec != NULL);
|
||||
if (options != NULL) {
|
||||
const int d = options->dithering_strength;
|
||||
const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
|
||||
const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
|
||||
if (f > 0) {
|
||||
int s;
|
||||
int all_amp = 0;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8QuantMatrix* const dqm = &dec->dqm_[s];
|
||||
if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
|
||||
const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
|
||||
dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
|
||||
}
|
||||
all_amp |= dqm->dither_;
|
||||
}
|
||||
if (all_amp != 0) {
|
||||
VP8InitRandom(&dec->dithering_rg_, 1.0f);
|
||||
dec->dither_ = 1;
|
||||
}
|
||||
}
|
||||
// potentially allow alpha dithering
|
||||
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
||||
if (dec->alpha_dithering_ > 100) {
|
||||
dec->alpha_dithering_ = 100;
|
||||
} else if (dec->alpha_dithering_ < 0) {
|
||||
dec->alpha_dithering_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
|
||||
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
||||
uint8_t dither[64];
|
||||
int i;
|
||||
for (i = 0; i < 8 * 8; ++i) {
|
||||
dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp);
|
||||
}
|
||||
VP8DitherCombine8x8(dither, dst, bps);
|
||||
}
|
||||
|
||||
static void DitherRow(VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
assert(dec->dither_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const VP8MBData* const data = ctx->mb_data_ + mb_x;
|
||||
const int cache_id = ctx->id_;
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
if (data->dither_ >= MIN_DITHER_AMP) {
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
|
||||
Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// This function is called after a row of macroblocks is finished decoding.
|
||||
// It also takes into account the following restrictions:
|
||||
// * In case of in-loop filtering, we must hold off sending some of the bottom
|
||||
// pixels as they are yet unfiltered. They will be when the next macroblock
|
||||
// row is decoded. Meanwhile, we must preserve them by rotating them in the
|
||||
// cache area. This doesn't hold for the very bottom row of the uncropped
|
||||
// picture of course.
|
||||
// * we must clip the remaining pixels against the cropping area. The VP8Io
|
||||
// struct must have the following fields set correctly before calling put():
|
||||
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
|
||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
||||
static int FinishRow(void* arg1, void* arg2) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)arg1;
|
||||
VP8Io* const io = (VP8Io*)arg2;
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride_;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int is_first_row = (mb_y == 0);
|
||||
const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
|
||||
|
||||
if (dec->mt_method_ == 2) {
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
|
||||
if (ctx->filter_row_) {
|
||||
FilterRow(dec);
|
||||
}
|
||||
|
||||
if (dec->dither_) {
|
||||
DitherRow(dec);
|
||||
}
|
||||
|
||||
if (io->put != NULL) {
|
||||
int y_start = MACROBLOCK_VPOS(mb_y);
|
||||
int y_end = MACROBLOCK_VPOS(mb_y + 1);
|
||||
if (!is_first_row) {
|
||||
y_start -= extra_y_rows;
|
||||
io->y = ydst;
|
||||
io->u = udst;
|
||||
io->v = vdst;
|
||||
} else {
|
||||
io->y = dec->cache_y_ + y_offset;
|
||||
io->u = dec->cache_u_ + uv_offset;
|
||||
io->v = dec->cache_v_ + uv_offset;
|
||||
}
|
||||
|
||||
if (!is_last_row) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
}
|
||||
// If dec->alpha_data_ is not NULL, we have some alpha plane present.
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Could not decode alpha data.");
|
||||
}
|
||||
}
|
||||
if (y_start < io->crop_top) {
|
||||
const int delta_y = io->crop_top - y_start;
|
||||
y_start = io->crop_top;
|
||||
assert(!(delta_y & 1));
|
||||
io->y += dec->cache_y_stride_ * delta_y;
|
||||
io->u += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
io->v += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
if (io->a != NULL) {
|
||||
io->a += io->width * delta_y;
|
||||
}
|
||||
}
|
||||
if (y_start < y_end) {
|
||||
io->y += io->crop_left;
|
||||
io->u += io->crop_left >> 1;
|
||||
io->v += io->crop_left >> 1;
|
||||
if (io->a != NULL) {
|
||||
io->a += io->crop_left;
|
||||
}
|
||||
io->mb_y = y_start - io->crop_top;
|
||||
io->mb_w = io->crop_right - io->crop_left;
|
||||
io->mb_h = y_end - y_start;
|
||||
ok = io->put(io);
|
||||
}
|
||||
}
|
||||
// rotate top samples if needed
|
||||
if (cache_id + 1 == dec->num_caches_) {
|
||||
if (!is_last_row) {
|
||||
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
|
||||
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#undef MACROBLOCK_VPOS
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int filter_row =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
if (dec->mt_method_ == 0) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
ReconstructRow(dec, ctx);
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPGetWorkerInterface()->Sync(worker);
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
if (dec->mt_method_ == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data_;
|
||||
ctx->mb_data_ = dec->mb_data_;
|
||||
dec->mb_data_ = tmp;
|
||||
} else {
|
||||
// perform reconstruction directly in main thread
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
}
|
||||
// (reconstruct)+filter in parallel
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Finish setting up the decoding parameter once user's setup() is called.
|
||||
|
||||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() no matter what.
|
||||
if (io->setup != NULL && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
}
|
||||
|
||||
// Disable filtering per user request
|
||||
if (io->bypass_filtering) {
|
||||
dec->filter_type_ = 0;
|
||||
}
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
//
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock
|
||||
// and filters one. It doesn't filter the chroma samples. Hence, we can
|
||||
// avoid doing the in-loop filtering before crop_top/crop_left position.
|
||||
// For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
|
||||
// Means: there's a dependency chain that goes all the way up to the
|
||||
// top-left corner of the picture (MB #0). We must filter all the previous
|
||||
// macroblocks.
|
||||
{
|
||||
const int extra_pixels = kFilterExtraRows[dec->filter_type_];
|
||||
if (dec->filter_type_ == 2) {
|
||||
// For complex filter, we need to preserve the dependency chain.
|
||||
dec->tl_mb_x_ = 0;
|
||||
dec->tl_mb_y_ = 0;
|
||||
} else {
|
||||
// For simple filter, we can filter only the cropped region.
|
||||
// We include 'extra_pixels' on the other side of the boundary, since
|
||||
// vertical or horizontal filtering of the previous macroblock can
|
||||
// modify some abutting pixels.
|
||||
dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
|
||||
dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
|
||||
if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
|
||||
if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
|
||||
}
|
||||
// We need some 'extra' pixels on the right/bottom.
|
||||
dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
|
||||
dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
|
||||
if (dec->br_mb_x_ > dec->mb_w_) {
|
||||
dec->br_mb_x_ = dec->mb_w_;
|
||||
}
|
||||
if (dec->br_mb_y_ > dec->mb_h_) {
|
||||
dec->br_mb_y_ = dec->mb_h_;
|
||||
}
|
||||
}
|
||||
PrecomputeFilterStrengths(dec);
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
if (dec->mt_method_ > 0) {
|
||||
ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
|
||||
}
|
||||
|
||||
if (io->teardown != NULL) {
|
||||
io->teardown(io);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
|
||||
//
|
||||
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
|
||||
// immediately, and needs to wait for first few rows of the next macroblock to
|
||||
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
|
||||
// on strength).
|
||||
// With two threads, the vertical positions of the rows being decoded are:
|
||||
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
|
||||
// Deblock: [ 0..11][12..27][28..43][44..59][...
|
||||
// If we use two threads and two caches of 16 pixels, the sequence would be:
|
||||
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
|
||||
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
|
||||
// The problem occurs during row [12..15!!] that both the decoding and
|
||||
// deblocking threads are writing simultaneously.
|
||||
// With 3 cache lines, one get a safe write pattern:
|
||||
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
|
||||
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
|
||||
// Note that multi-threaded output _without_ deblocking can make use of two
|
||||
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
|
||||
// and output process have non-concurrent writing:
|
||||
// Decode: [ 0..15][16..31][ 0..15][16..31][...
|
||||
// io->put: [ 0..15][16..31][ 0..15][...
|
||||
|
||||
#define MT_CACHE_LINES 3
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id_ = 0;
|
||||
if (dec->mt_method_ > 0) {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
if (!WebPGetWorkerInterface()->Reset(worker)) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"thread initialization failed.");
|
||||
}
|
||||
worker->data1 = dec;
|
||||
worker->data2 = (void*)&dec->thread_ctx_.io_;
|
||||
worker->hook = FinishRow;
|
||||
dec->num_caches_ =
|
||||
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
||||
} else {
|
||||
dec->num_caches_ = ST_CACHE_LINES;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height) {
|
||||
if (options == NULL || options->use_threads == 0) {
|
||||
return 0;
|
||||
}
|
||||
(void)headers;
|
||||
(void)width;
|
||||
(void)height;
|
||||
assert(headers == NULL || !headers->is_lossless);
|
||||
#if defined(WEBP_USE_THREAD)
|
||||
if (width >= MIN_WIDTH_FOR_THREADS) return 2;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef MT_CACHE_LINES
|
||||
#undef ST_CACHE_LINES
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Memory setup
|
||||
|
||||
static int AllocateMemory(VP8Decoder* const dec) {
|
||||
const int num_caches = dec->num_caches_;
|
||||
const int mb_w = dec->mb_w_;
|
||||
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
|
||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||
const size_t top_size = sizeof(VP8TopSamples) * mb_w;
|
||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||
const size_t f_info_size =
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
const size_t mb_data_size =
|
||||
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
// alpha_size is the only one that scales as width x height.
|
||||
const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + mb_data_size
|
||||
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
uint8_t* mem;
|
||||
|
||||
if (!CheckSizeOverflow(needed)) return 0; // check for overflow
|
||||
if (needed > dec->mem_size_) {
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_size_ = 0;
|
||||
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem_ == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"no memory during frame initialization.");
|
||||
}
|
||||
// down-cast is ok, thanks to WebPSafeMalloc() above.
|
||||
dec->mem_size_ = (size_t)needed;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)dec->mem_;
|
||||
dec->intra_t_ = mem;
|
||||
mem += intra_pred_mode_size;
|
||||
|
||||
dec->yuv_t_ = (VP8TopSamples*)mem;
|
||||
mem += top_size;
|
||||
|
||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||
mem += mb_info_size;
|
||||
|
||||
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
|
||||
mem += f_info_size;
|
||||
dec->thread_ctx_.id_ = 0;
|
||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||
if (dec->filter_type_ > 0 && dec->mt_method_ > 0) {
|
||||
// secondary cache line. The deblocking process need to make use of the
|
||||
// filtering strength from previous macroblock row, while the new ones
|
||||
// are being decoded in parallel. We'll just swap the pointers.
|
||||
dec->thread_ctx_.f_info_ += mb_w;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)WEBP_ALIGN(mem);
|
||||
assert((yuv_size & WEBP_ALIGN_CST) == 0);
|
||||
dec->yuv_b_ = mem;
|
||||
mem += yuv_size;
|
||||
|
||||
dec->mb_data_ = (VP8MBData*)mem;
|
||||
dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
|
||||
if (dec->mt_method_ == 2) {
|
||||
dec->thread_ctx_.mb_data_ += mb_w;
|
||||
}
|
||||
mem += mb_data_size;
|
||||
|
||||
dec->cache_y_stride_ = 16 * mb_w;
|
||||
dec->cache_uv_stride_ = 8 * mb_w;
|
||||
{
|
||||
const int extra_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int extra_y = extra_rows * dec->cache_y_stride_;
|
||||
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
|
||||
dec->cache_y_ = mem + extra_y;
|
||||
dec->cache_u_ = dec->cache_y_
|
||||
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
|
||||
dec->cache_v_ = dec->cache_u_
|
||||
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
mem += cache_size;
|
||||
|
||||
// alpha plane
|
||||
dec->alpha_plane_ = alpha_size ? mem : NULL;
|
||||
mem += alpha_size;
|
||||
assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
|
||||
|
||||
// note: left/top-info is initialized once for all.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
|
||||
// initialize top
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||
// prepare 'io'
|
||||
io->mb_y = 0;
|
||||
io->y = dec->cache_y_;
|
||||
io->u = dec->cache_u_;
|
||||
io->v = dec->cache_v_;
|
||||
io->y_stride = dec->cache_y_stride_;
|
||||
io->uv_stride = dec->cache_uv_stride_;
|
||||
io->a = NULL;
|
||||
}
|
||||
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
|
||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||
if (!AllocateMemory(dec)) return 0;
|
||||
InitIo(dec, io);
|
||||
VP8DspInit(); // Init critical function pointers and look-up tables.
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,908 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Incremental decoding
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
// In append mode, buffer allocations increase as multiples of this value.
|
||||
// Needs to be a power of 2.
|
||||
#define CHUNK_SIZE 4096
|
||||
#define MAX_MB_SIZE 4096
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Data structures for memory and states
|
||||
|
||||
// Decoding states. State normally flows as:
|
||||
// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
|
||||
// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
|
||||
// If there is any error the decoder goes into state ERROR.
|
||||
typedef enum {
|
||||
STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
|
||||
STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
|
||||
STATE_VP8_PARTS0,
|
||||
STATE_VP8_DATA,
|
||||
STATE_VP8L_HEADER,
|
||||
STATE_VP8L_DATA,
|
||||
STATE_DONE,
|
||||
STATE_ERROR
|
||||
} DecState;
|
||||
|
||||
// Operating state for the MemBuffer
|
||||
typedef enum {
|
||||
MEM_MODE_NONE = 0,
|
||||
MEM_MODE_APPEND,
|
||||
MEM_MODE_MAP
|
||||
} MemBufferMode;
|
||||
|
||||
// storage for partition #0 and partial data (in a rolling fashion)
|
||||
typedef struct {
|
||||
MemBufferMode mode_; // Operation mode
|
||||
size_t start_; // start location of the data to be decoded
|
||||
size_t end_; // end location
|
||||
size_t buf_size_; // size of the allocated buffer
|
||||
uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
|
||||
|
||||
size_t part0_size_; // size of partition #0
|
||||
const uint8_t* part0_buf_; // buffer to store partition #0
|
||||
} MemBuffer;
|
||||
|
||||
struct WebPIDecoder {
|
||||
DecState state_; // current decoding state
|
||||
WebPDecParams params_; // Params to store output info
|
||||
int is_lossless_; // for down-casting 'dec_'.
|
||||
void* dec_; // either a VP8Decoder or a VP8LDecoder instance
|
||||
VP8Io io_;
|
||||
|
||||
MemBuffer mem_; // input memory buffer.
|
||||
WebPDecBuffer output_; // output buffer (when no external one is supplied,
|
||||
// or if the external one has slow-memory)
|
||||
WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.
|
||||
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
|
||||
|
||||
int last_mb_y_; // last row reached for intra-mode decoding
|
||||
};
|
||||
|
||||
// MB context to restore in case VP8DecodeMB() fails
|
||||
typedef struct {
|
||||
VP8MB left_;
|
||||
VP8MB info_;
|
||||
VP8BitReader token_br_;
|
||||
} MBContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MemBuffer: incoming data handling
|
||||
|
||||
static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
}
|
||||
|
||||
// Check if we need to preserve the compressed alpha data, as it may not have
|
||||
// been decoded yet.
|
||||
static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
// We haven't parsed the headers yet, so we don't know whether the image is
|
||||
// lossy or lossless. This also means that we haven't parsed the ALPH chunk.
|
||||
return 0;
|
||||
}
|
||||
if (idec->is_lossless_) {
|
||||
return 0; // ALPH chunk is not present for lossless images.
|
||||
} else {
|
||||
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER.
|
||||
return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const new_base = mem->buf_ + mem->start_;
|
||||
// note: for VP8, setting up idec->io_ is only really needed at the beginning
|
||||
// of the decoding, till partition #0 is complete.
|
||||
idec->io_.data = new_base;
|
||||
idec->io_.data_size = MemDataSize(mem);
|
||||
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
const uint32_t last_part = dec->num_parts_minus_one_;
|
||||
if (offset != 0) {
|
||||
uint32_t p;
|
||||
for (p = 0; p <= last_part; ++p) {
|
||||
VP8RemapBitReader(dec->parts_ + p, offset);
|
||||
}
|
||||
// Remap partition #0 data pointer to new offset, but only in MAP
|
||||
// mode (in APPEND mode, partition #0 is copied into a fixed memory).
|
||||
if (mem->mode_ == MEM_MODE_MAP) {
|
||||
VP8RemapBitReader(&dec->br_, offset);
|
||||
}
|
||||
}
|
||||
{
|
||||
const uint8_t* const last_start = dec->parts_[last_part].buf_;
|
||||
VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
|
||||
mem->buf_ + mem->end_ - last_start);
|
||||
}
|
||||
if (NeedCompressedAlpha(idec)) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
dec->alpha_data_ += offset;
|
||||
if (alph_dec != NULL && alph_dec->vp8l_dec_ != NULL) {
|
||||
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
|
||||
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
|
||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
|
||||
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
|
||||
dec->alpha_data_ + ALPHA_HEADER_LEN,
|
||||
dec->alpha_data_size_ - ALPHA_HEADER_LEN);
|
||||
} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
|
||||
// Nothing special to do in this case.
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Resize lossless bitreader
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
|
||||
// size if required and also updates VP8BitReader's if new memory is allocated.
|
||||
static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const int need_compressed_alpha = NeedCompressedAlpha(idec);
|
||||
const uint8_t* const old_start =
|
||||
(mem->buf_ == NULL) ? NULL : mem->buf_ + mem->start_;
|
||||
const uint8_t* const old_base =
|
||||
need_compressed_alpha ? dec->alpha_data_ : old_start;
|
||||
assert(mem->buf_ != NULL || mem->start_ == 0);
|
||||
assert(mem->mode_ == MEM_MODE_APPEND);
|
||||
if (data_size > MAX_CHUNK_PAYLOAD) {
|
||||
// security safeguard: trying to allocate more than what the format
|
||||
// allows for a chunk should be considered a smoke smell.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
|
||||
const size_t new_mem_start = old_start - old_base;
|
||||
const size_t current_size = MemDataSize(mem) + new_mem_start;
|
||||
const uint64_t new_size = (uint64_t)current_size + data_size;
|
||||
const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
|
||||
uint8_t* const new_buf =
|
||||
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
|
||||
if (new_buf == NULL) return 0;
|
||||
if (old_base != NULL) memcpy(new_buf, old_base, current_size);
|
||||
WebPSafeFree(mem->buf_);
|
||||
mem->buf_ = new_buf;
|
||||
mem->buf_size_ = (size_t)extra_size;
|
||||
mem->start_ = new_mem_start;
|
||||
mem->end_ = current_size;
|
||||
}
|
||||
|
||||
assert(mem->buf_ != NULL);
|
||||
memcpy(mem->buf_ + mem->end_, data, data_size);
|
||||
mem->end_ += data_size;
|
||||
assert(mem->end_ <= mem->buf_size_);
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_buf = mem->buf_;
|
||||
const uint8_t* const old_start =
|
||||
(old_buf == NULL) ? NULL : old_buf + mem->start_;
|
||||
assert(old_buf != NULL || mem->start_ == 0);
|
||||
assert(mem->mode_ == MEM_MODE_MAP);
|
||||
|
||||
if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
|
||||
|
||||
mem->buf_ = (uint8_t*)data;
|
||||
mem->end_ = mem->buf_size_ = data_size;
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void InitMemBuffer(MemBuffer* const mem) {
|
||||
mem->mode_ = MEM_MODE_NONE;
|
||||
mem->buf_ = NULL;
|
||||
mem->buf_size_ = 0;
|
||||
mem->part0_buf_ = NULL;
|
||||
mem->part0_size_ = 0;
|
||||
}
|
||||
|
||||
static void ClearMemBuffer(MemBuffer* const mem) {
|
||||
assert(mem);
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
WebPSafeFree(mem->buf_);
|
||||
WebPSafeFree((void*)mem->part0_buf_);
|
||||
}
|
||||
}
|
||||
|
||||
static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
||||
if (mem->mode_ == MEM_MODE_NONE) {
|
||||
mem->mode_ = expected; // switch to the expected mode
|
||||
} else if (mem->mode_ != expected) {
|
||||
return 0; // we mixed the modes => error
|
||||
}
|
||||
assert(mem->mode_ == expected); // mode is ok
|
||||
return 1;
|
||||
}
|
||||
|
||||
// To be called last.
|
||||
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||
const WebPDecoderOptions* const options = idec->params_.options;
|
||||
WebPDecBuffer* const output = idec->params_.output;
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
if (options != NULL && options->flip) {
|
||||
const VP8StatusCode status = WebPFlipBuffer(output);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
}
|
||||
if (idec->final_output_ != NULL) {
|
||||
WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
*output = *idec->final_output_;
|
||||
idec->final_output_ = NULL;
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Macroblock-decoding contexts
|
||||
|
||||
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
|
||||
MBContext* const context) {
|
||||
context->left_ = dec->mb_info_[-1];
|
||||
context->info_ = dec->mb_info_[dec->mb_x_];
|
||||
context->token_br_ = *token_br;
|
||||
}
|
||||
|
||||
static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
VP8BitReader* const token_br) {
|
||||
dec->mb_info_[-1] = context->left_;
|
||||
dec->mb_info_[dec->mb_x_] = context->info_;
|
||||
*token_br = context->token_br_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
idec->state_ = STATE_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void ChangeState(WebPIDecoder* const idec, DecState new_state,
|
||||
size_t consumed_bytes) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
idec->state_ = new_state;
|
||||
mem->start_ += consumed_bytes;
|
||||
assert(mem->start_ <= mem->end_);
|
||||
idec->io_.data = mem->buf_ + mem->start_;
|
||||
idec->io_.data_size = MemDataSize(mem);
|
||||
}
|
||||
|
||||
// Headers
|
||||
static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* data = mem->buf_ + mem->start_;
|
||||
size_t curr_size = MemDataSize(mem);
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = curr_size;
|
||||
headers.have_all_data = 0;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
|
||||
} else if (status != VP8_STATUS_OK) {
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
idec->chunk_size_ = headers.compressed_size;
|
||||
idec->is_lossless_ = headers.is_lossless;
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Decoder* const dec = VP8New();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
ChangeState(idec, STATE_VP8_HEADER, headers.offset);
|
||||
} else {
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
int width, height;
|
||||
uint32_t bits;
|
||||
|
||||
if (curr_size < VP8_FRAME_HEADER_SIZE) {
|
||||
// Not enough data bytes to extract VP8 Frame Header.
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
|
||||
bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;
|
||||
|
||||
idec->io_.data = data;
|
||||
idec->io_.data_size = curr_size;
|
||||
idec->state_ = STATE_VP8_PARTS0;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Partition #0
|
||||
static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const size_t part_size = br->buf_end_ - br->buf_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
assert(!idec->is_lossless_);
|
||||
assert(mem->part0_buf_ == NULL);
|
||||
// the following is a format limitation, no need for runtime check:
|
||||
assert(part_size <= mem->part0_size_);
|
||||
if (part_size == 0) { // can't have zero-size partition #0
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
// We copy and grab ownership of the partition #0 data.
|
||||
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
|
||||
if (part0_buf == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(part0_buf, br->buf_, part_size);
|
||||
mem->part0_buf_ = part0_buf;
|
||||
VP8BitReaderSetBuffer(br, part0_buf, part_size);
|
||||
} else {
|
||||
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
||||
}
|
||||
mem->start_ += part_size;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8Io* const io = &idec->io_;
|
||||
const WebPDecParams* const params = &idec->params_;
|
||||
WebPDecBuffer* const output = params->output;
|
||||
|
||||
// Wait till we have enough data for the whole partition #0
|
||||
if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!VP8GetHeaders(dec, io)) {
|
||||
const VP8StatusCode status = dec->status_;
|
||||
if (status == VP8_STATUS_SUSPENDED ||
|
||||
status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
// treating NOT_ENOUGH_DATA as SUSPENDED state
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
// Allocate/Verify output buffer now
|
||||
dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
|
||||
output);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
// This change must be done before calling VP8InitFrame()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
|
||||
io->width, io->height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
|
||||
dec->status_ = CopyParts0Data(idec);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
// Finish setting up the decoding parameters. Will call io->setup().
|
||||
if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
// Note: past this point, teardown() must always be called
|
||||
// in case of error.
|
||||
idec->state_ = STATE_VP8_DATA;
|
||||
// Allocate memory and prepare everything.
|
||||
if (!VP8InitFrame(dec, io)) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Remaining partitions
|
||||
static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8Io* const io = &idec->io_;
|
||||
|
||||
// Make sure partition #0 has been read before, to set dec to ready_.
|
||||
if (!dec->ready_) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
||||
if (idec->last_mb_y_ != dec->mb_y_) {
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
// note: normally, error shouldn't occur since we already have the whole
|
||||
// partition0 available here in DecodeRemaining(). Reaching EOF while
|
||||
// reading intra modes really means a BITSTREAM_ERROR.
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
idec->last_mb_y_ = dec->mb_y_;
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||
MBContext context;
|
||||
SaveContext(dec, token_br, &context);
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
// We shouldn't fail when MAX_MB data was available
|
||||
if (dec->num_parts_minus_one_ == 0 &&
|
||||
MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
// Synchronize the threads.
|
||||
if (dec->mt_method_ > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
}
|
||||
RestoreContext(&context, dec, token_br);
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
// Release buffer only if there is only one partition
|
||||
if (dec->num_parts_minus_one_ == 0) {
|
||||
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
|
||||
assert(idec->mem_.start_ <= idec->mem_.end_);
|
||||
}
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
}
|
||||
// Synchronize the thread and check for errors.
|
||||
if (!VP8ExitCritical(dec, io)) {
|
||||
idec->state_ = STATE_ERROR; // prevent re-entry in IDecError
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->ready_ = 0;
|
||||
return FinishDecoding(idec);
|
||||
}
|
||||
|
||||
static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,
|
||||
VP8StatusCode status) {
|
||||
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
const WebPDecParams* const params = &idec->params_;
|
||||
WebPDecBuffer* const output = params->output;
|
||||
size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// Wait until there's enough data for decoding header.
|
||||
if (curr_size < (idec->chunk_size_ >> 3)) {
|
||||
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
|
||||
if (!VP8LDecodeHeader(dec, io)) {
|
||||
if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
|
||||
curr_size < idec->chunk_size_) {
|
||||
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
// Allocate/verify output buffer now.
|
||||
dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
|
||||
output);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
idec->state_ = STATE_VP8L_DATA;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// Switch to incremental decoding if we don't have all the bytes available.
|
||||
dec->incremental_ = (curr_size < idec->chunk_size_);
|
||||
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
|
||||
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
|
||||
: FinishDecoding(idec);
|
||||
}
|
||||
|
||||
// Main decoding loop
|
||||
static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
VP8StatusCode status = VP8_STATUS_SUSPENDED;
|
||||
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
status = DecodeWebPHeaders(idec);
|
||||
} else {
|
||||
if (idec->dec_ == NULL) {
|
||||
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||
}
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_HEADER) {
|
||||
status = DecodeVP8FrameHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_PARTS0) {
|
||||
status = DecodePartition0(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||
}
|
||||
status = DecodeRemaining(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8L_HEADER) {
|
||||
status = DecodeVP8LHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8L_DATA) {
|
||||
status = DecodeVP8LData(idec);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Internal constructor
|
||||
|
||||
static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
|
||||
const WebPBitstreamFeatures* const features) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idec->state_ = STATE_WEBP_HEADER;
|
||||
idec->chunk_size_ = 0;
|
||||
|
||||
idec->last_mb_y_ = -1;
|
||||
|
||||
InitMemBuffer(&idec->mem_);
|
||||
WebPInitDecBuffer(&idec->output_);
|
||||
VP8InitIo(&idec->io_);
|
||||
|
||||
WebPResetDecParams(&idec->params_);
|
||||
if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
|
||||
idec->params_.output = &idec->output_;
|
||||
idec->final_output_ = output_buffer;
|
||||
if (output_buffer != NULL) {
|
||||
idec->params_.output->colorspace = output_buffer->colorspace;
|
||||
}
|
||||
} else {
|
||||
idec->params_.output = output_buffer;
|
||||
idec->final_output_ = NULL;
|
||||
}
|
||||
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
|
||||
|
||||
return idec;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||
return NewDecoder(output_buffer, NULL);
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config) {
|
||||
WebPIDecoder* idec;
|
||||
WebPBitstreamFeatures tmp_features;
|
||||
WebPBitstreamFeatures* const features =
|
||||
(config == NULL) ? &tmp_features : &config->input;
|
||||
memset(&tmp_features, 0, sizeof(tmp_features));
|
||||
|
||||
// Parse the bitstream's features, if requested:
|
||||
if (data != NULL && data_size > 0) {
|
||||
if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of the incremental decoder
|
||||
idec = (config != NULL) ? NewDecoder(&config->output, features)
|
||||
: NewDecoder(NULL, features);
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Finish initialization
|
||||
if (config != NULL) {
|
||||
idec->params_.options = &config->options;
|
||||
}
|
||||
return idec;
|
||||
}
|
||||
|
||||
void WebPIDelete(WebPIDecoder* idec) {
|
||||
if (idec == NULL) return;
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
VP8Delete((VP8Decoder*)idec->dec_);
|
||||
} else {
|
||||
VP8LDelete((VP8LDecoder*)idec->dec_);
|
||||
}
|
||||
}
|
||||
ClearMemBuffer(&idec->mem_);
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
WebPSafeFree(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Wrapper toward WebPINewDecoder
|
||||
|
||||
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer,
|
||||
size_t output_buffer_size, int output_stride) {
|
||||
const int is_external_memory = (output_buffer != NULL) ? 1 : 0;
|
||||
WebPIDecoder* idec;
|
||||
|
||||
if (csp >= MODE_YUV) return NULL;
|
||||
if (is_external_memory == 0) { // Overwrite parameters to sane values.
|
||||
output_buffer_size = 0;
|
||||
output_stride = 0;
|
||||
} else { // A buffer was passed. Validate the other params.
|
||||
if (output_stride == 0 || output_buffer_size == 0) {
|
||||
return NULL; // invalid parameter.
|
||||
}
|
||||
}
|
||||
idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
idec->output_.colorspace = csp;
|
||||
idec->output_.is_external_memory = is_external_memory;
|
||||
idec->output_.u.RGBA.rgba = output_buffer;
|
||||
idec->output_.u.RGBA.stride = output_stride;
|
||||
idec->output_.u.RGBA.size = output_buffer_size;
|
||||
return idec;
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride) {
|
||||
const int is_external_memory = (luma != NULL) ? 1 : 0;
|
||||
WebPIDecoder* idec;
|
||||
WEBP_CSP_MODE colorspace;
|
||||
|
||||
if (is_external_memory == 0) { // Overwrite parameters to sane values.
|
||||
luma_size = u_size = v_size = a_size = 0;
|
||||
luma_stride = u_stride = v_stride = a_stride = 0;
|
||||
u = v = a = NULL;
|
||||
colorspace = MODE_YUVA;
|
||||
} else { // A luma buffer was passed. Validate the other parameters.
|
||||
if (u == NULL || v == NULL) return NULL;
|
||||
if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL;
|
||||
if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL;
|
||||
if (a != NULL) {
|
||||
if (a_size == 0 || a_stride == 0) return NULL;
|
||||
}
|
||||
colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
|
||||
}
|
||||
|
||||
idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
|
||||
idec->output_.colorspace = colorspace;
|
||||
idec->output_.is_external_memory = is_external_memory;
|
||||
idec->output_.u.YUVA.y = luma;
|
||||
idec->output_.u.YUVA.y_stride = luma_stride;
|
||||
idec->output_.u.YUVA.y_size = luma_size;
|
||||
idec->output_.u.YUVA.u = u;
|
||||
idec->output_.u.YUVA.u_stride = u_stride;
|
||||
idec->output_.u.YUVA.u_size = u_size;
|
||||
idec->output_.u.YUVA.v = v;
|
||||
idec->output_.u.YUVA.v_stride = v_stride;
|
||||
idec->output_.u.YUVA.v_size = v_size;
|
||||
idec->output_.u.YUVA.a = a;
|
||||
idec->output_.u.YUVA.a_stride = a_stride;
|
||||
idec->output_.u.YUVA.a_size = a_size;
|
||||
return idec;
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride) {
|
||||
return WebPINewYUVA(luma, luma_size, luma_stride,
|
||||
u, u_size, u_stride,
|
||||
v, v_size, v_stride,
|
||||
NULL, 0, 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
|
||||
assert(idec);
|
||||
if (idec->state_ == STATE_ERROR) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (idec->state_ == STATE_DONE) {
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPIAppend(WebPIDecoder* idec,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
VP8StatusCode status;
|
||||
if (idec == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
status = IDecCheckStatus(idec);
|
||||
if (status != VP8_STATUS_SUSPENDED) {
|
||||
return status;
|
||||
}
|
||||
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
|
||||
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
// Append data to memory buffer
|
||||
if (!AppendToMemBuffer(idec, data, data_size)) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
return IDecode(idec);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
VP8StatusCode status;
|
||||
if (idec == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
status = IDecCheckStatus(idec);
|
||||
if (status != VP8_STATUS_SUSPENDED) {
|
||||
return status;
|
||||
}
|
||||
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
|
||||
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
// Make the memory buffer point to the new buffer
|
||||
if (!RemapMemBuffer(idec, data, data_size)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
return IDecode(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
|
||||
if (idec == NULL || idec->dec_ == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (idec->state_ <= STATE_VP8_PARTS0) {
|
||||
return NULL;
|
||||
}
|
||||
if (idec->final_output_ != NULL) {
|
||||
return NULL; // not yet slow-copied
|
||||
}
|
||||
return idec->params_.output;
|
||||
}
|
||||
|
||||
const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
||||
int* left, int* top,
|
||||
int* width, int* height) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (left != NULL) *left = 0;
|
||||
if (top != NULL) *top = 0;
|
||||
if (src != NULL) {
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = idec->params_.last_y;
|
||||
} else {
|
||||
if (width != NULL) *width = 0;
|
||||
if (height != NULL) *height = 0;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace >= MODE_YUV) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = src->height;
|
||||
if (stride != NULL) *stride = src->u.RGBA.stride;
|
||||
|
||||
return src->u.RGBA.rgba;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height,
|
||||
int* stride, int* uv_stride, int* a_stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace < MODE_YUV) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||
if (u != NULL) *u = src->u.YUVA.u;
|
||||
if (v != NULL) *v = src->u.YUVA.v;
|
||||
if (a != NULL) *a = src->u.YUVA.a;
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = src->height;
|
||||
if (stride != NULL) *stride = src->u.YUVA.y_stride;
|
||||
if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
|
||||
if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
|
||||
|
||||
return src->u.YUVA.y;
|
||||
}
|
||||
|
||||
int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoPutHook put,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data) {
|
||||
if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
idec->io_.put = put;
|
||||
idec->io_.setup = setup;
|
||||
idec->io_.teardown = teardown;
|
||||
idec->io_.opaque = user_data;
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,662 @@
|
|||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// functions for sample output.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/dsp/yuv.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main YUV<->RGB conversion functions
|
||||
|
||||
static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPYUVABuffer* const buf = &output->u.YUVA;
|
||||
uint8_t* const y_dst = buf->y + (size_t)io->mb_y * buf->y_stride;
|
||||
uint8_t* const u_dst = buf->u + (size_t)(io->mb_y >> 1) * buf->u_stride;
|
||||
uint8_t* const v_dst = buf->v + (size_t)(io->mb_y >> 1) * buf->v_stride;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
const int uv_h = (mb_h + 1) / 2;
|
||||
WebPCopyPlane(io->y, io->y_stride, y_dst, buf->y_stride, mb_w, mb_h);
|
||||
WebPCopyPlane(io->u, io->uv_stride, u_dst, buf->u_stride, uv_w, uv_h);
|
||||
WebPCopyPlane(io->v, io->uv_stride, v_dst, buf->v_stride, uv_w, uv_h);
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
// Point-sampling U/V sampler.
|
||||
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* const output = p->output;
|
||||
WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const dst = buf->rgba + (size_t)io->mb_y * buf->stride;
|
||||
WebPSamplerProcessPlane(io->y, io->y_stride,
|
||||
io->u, io->v, io->uv_stride,
|
||||
dst, buf->stride, io->mb_w, io->mb_h,
|
||||
WebPSamplers[output->colorspace]);
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Fancy upsampling
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_lines_out = io->mb_h; // a priori guess
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (size_t)io->mb_y * buf->stride;
|
||||
WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
|
||||
const uint8_t* cur_y = io->y;
|
||||
const uint8_t* cur_u = io->u;
|
||||
const uint8_t* cur_v = io->v;
|
||||
const uint8_t* top_u = p->tmp_u;
|
||||
const uint8_t* top_v = p->tmp_v;
|
||||
int y = io->mb_y;
|
||||
const int y_end = io->mb_y + io->mb_h;
|
||||
const int mb_w = io->mb_w;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
|
||||
if (y == 0) {
|
||||
// First line is special cased. We mirror the u/v samples at boundary.
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
++num_lines_out;
|
||||
}
|
||||
// Loop over each output pairs of row.
|
||||
for (; y + 2 < y_end; y += 2) {
|
||||
top_u = cur_u;
|
||||
top_v = cur_v;
|
||||
cur_u += io->uv_stride;
|
||||
cur_v += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
cur_y += 2 * io->y_stride;
|
||||
upsample(cur_y - io->y_stride, cur_y,
|
||||
top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
}
|
||||
// move to last row
|
||||
cur_y += io->y_stride;
|
||||
if (io->crop_top + y_end < io->crop_bottom) {
|
||||
// Save the unfinished samples for next call (as we're not done yet).
|
||||
memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
|
||||
memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
|
||||
memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
|
||||
// The fancy upsampler leaves a row unfinished behind
|
||||
// (except for the very last row)
|
||||
num_lines_out--;
|
||||
} else {
|
||||
// Process the very last row of even-sized picture
|
||||
if (!(y_end & 1)) {
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
||||
dst + buf->stride, NULL, mb_w);
|
||||
}
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
#endif /* FANCY_UPSAMPLING */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) {
|
||||
int j;
|
||||
for (j = 0; j < h; ++j) {
|
||||
memset(dst, 0xff, w * sizeof(*dst));
|
||||
dst += stride;
|
||||
}
|
||||
}
|
||||
|
||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
uint8_t* dst = buf->a + (size_t)io->mb_y * buf->a_stride;
|
||||
int j;
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == mb_h);
|
||||
if (alpha != NULL) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||
alpha += io->width;
|
||||
dst += buf->a_stride;
|
||||
}
|
||||
} else if (buf->a != NULL) {
|
||||
// the user requested alpha, but there is none, set it to opaque.
|
||||
FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
const uint8_t** alpha, int* const num_rows) {
|
||||
int start_y = io->mb_y;
|
||||
*num_rows = io->mb_h;
|
||||
|
||||
// Compensate for the 1-line delay of the fancy upscaler.
|
||||
// This is similar to EmitFancyRGB().
|
||||
if (io->fancy_upsampling) {
|
||||
if (start_y == 0) {
|
||||
// We don't process the last row yet. It'll be done during the next call.
|
||||
--*num_rows;
|
||||
} else {
|
||||
--start_y;
|
||||
// Fortunately, *alpha data is persistent, so we can go back
|
||||
// one row and finish alpha blending, now that the fancy upscaler
|
||||
// completed the YUV->RGB interpolation.
|
||||
*alpha -= io->width;
|
||||
}
|
||||
if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
|
||||
// If it's the very last call, we process all the remaining rows!
|
||||
*num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||
}
|
||||
}
|
||||
return start_y;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
|
||||
num_rows, dst, buf->stride);
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
// has_alpha is true if there's non-trivial alpha to premultiply with.
|
||||
if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const size_t start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
#if (WEBP_SWAP_16BIT_CSP == 1)
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
int i, j;
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_value = alpha[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// YUV rescaling (no final RGB conversion needed)
|
||||
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
static int Rescale(const uint8_t* src, int src_stride,
|
||||
int new_lines, WebPRescaler* const wrk) {
|
||||
int num_lines_out = 0;
|
||||
while (new_lines > 0) { // import new contributions of source rows.
|
||||
const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
|
||||
src += lines_in * src_stride;
|
||||
new_lines -= lines_in;
|
||||
num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
WebPRescaler* const scaler = p->scaler_y;
|
||||
int num_lines_out = 0;
|
||||
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
||||
// Before rescaling, we premultiply the luma directly into the io->y
|
||||
// internal buffer. This is OK since these samples are not used for
|
||||
// intra-prediction (the top samples are saved in cache_y_/u_/v_).
|
||||
// But we need to cast the const away, though.
|
||||
WebPMultRows((uint8_t*)io->y, io->y_stride,
|
||||
io->a, io->width, io->mb_w, mb_h, 0);
|
||||
}
|
||||
num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
|
||||
Rescale(io->u, io->uv_stride, uv_mb_h, p->scaler_u);
|
||||
Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v);
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
uint8_t* const dst_a = buf->a + (size_t)p->last_y * buf->a_stride;
|
||||
if (io->a != NULL) {
|
||||
uint8_t* const dst_y = buf->y + (size_t)p->last_y * buf->y_stride;
|
||||
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a);
|
||||
assert(expected_num_lines_out == num_lines_out);
|
||||
if (num_lines_out > 0) { // unmultiply the Y
|
||||
WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride,
|
||||
p->scaler_a->dst_width, num_lines_out, 1);
|
||||
}
|
||||
} else if (buf->a != NULL) {
|
||||
// the user requested alpha, but there is none, set it to opaque.
|
||||
assert(p->last_y + expected_num_lines_out <= io->scaled_height);
|
||||
FillAlphaPlane(dst_a, io->scaled_width, expected_num_lines_out,
|
||||
buf->a_stride);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
const int uv_out_width = (out_width + 1) >> 1;
|
||||
const int uv_out_height = (out_height + 1) >> 1;
|
||||
const int uv_in_width = (io->mb_w + 1) >> 1;
|
||||
const int uv_in_height = (io->mb_h + 1) >> 1;
|
||||
// scratch memory for luma rescaler
|
||||
const size_t work_size = 2 * (size_t)out_width;
|
||||
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
|
||||
uint64_t total_size;
|
||||
size_t rescaler_size;
|
||||
rescaler_t* work;
|
||||
WebPRescaler* scalers;
|
||||
const int num_rescalers = has_alpha ? 4 : 3;
|
||||
|
||||
total_size = ((uint64_t)work_size + 2 * uv_work_size) * sizeof(*work);
|
||||
if (has_alpha) {
|
||||
total_size += (uint64_t)work_size * sizeof(*work);
|
||||
}
|
||||
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||
total_size += rescaler_size;
|
||||
if (!CheckSizeOverflow(total_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (rescaler_t*)p->memory;
|
||||
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN(
|
||||
(const uint8_t*)work + total_size - rescaler_size);
|
||||
p->scaler_y = &scalers[0];
|
||||
p->scaler_u = &scalers[1];
|
||||
p->scaler_v = &scalers[2];
|
||||
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||
buf->y, out_width, out_height, buf->y_stride, 1,
|
||||
work) ||
|
||||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
|
||||
work + work_size) ||
|
||||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
|
||||
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
|
||||
work + work_size + uv_work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit = EmitRescaledYUV;
|
||||
|
||||
if (has_alpha) {
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||
buf->a, out_width, out_height, buf->a_stride, 1,
|
||||
work + work_size + 2 * uv_work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit_alpha = EmitRescaledAlphaYUV;
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RGBA rescaling
|
||||
|
||||
static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
const WebPYUV444Converter convert =
|
||||
WebPYUV444Converters[p->output->colorspace];
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
int num_lines_out = 0;
|
||||
// For RGB rescaling, because of the YUV420, current scan position
|
||||
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_y) &&
|
||||
WebPRescalerHasPendingOutput(p->scaler_u)) {
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
assert(p->scaler_u->y_accum == p->scaler_v->y_accum);
|
||||
WebPRescalerExportRow(p->scaler_y);
|
||||
WebPRescalerExportRow(p->scaler_u);
|
||||
WebPRescalerExportRow(p->scaler_v);
|
||||
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst,
|
||||
dst, p->scaler_y->dst_width);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
int j = 0, uv_j = 0;
|
||||
int num_lines_out = 0;
|
||||
while (j < mb_h) {
|
||||
const int y_lines_in =
|
||||
WebPRescalerImport(p->scaler_y, mb_h - j,
|
||||
io->y + (size_t)j * io->y_stride, io->y_stride);
|
||||
j += y_lines_in;
|
||||
if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) {
|
||||
const int u_lines_in = WebPRescalerImport(
|
||||
p->scaler_u, uv_mb_h - uv_j, io->u + (size_t)uv_j * io->uv_stride,
|
||||
io->uv_stride);
|
||||
const int v_lines_in = WebPRescalerImport(
|
||||
p->scaler_v, uv_mb_h - uv_j, io->v + (size_t)uv_j * io->uv_stride,
|
||||
io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
uv_j += u_lines_in;
|
||||
}
|
||||
num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t non_opaque = 0;
|
||||
const int width = p->scaler_a->dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(p->scaler_a);
|
||||
non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && non_opaque) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
|
||||
int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (size_t)y_pos * buf->stride;
|
||||
#if (WEBP_SWAP_16BIT_CSP == 1)
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
int num_lines_out = 0;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a->dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
int i;
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_value = p->scaler_a->dst[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha_dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && alpha_mask != 0x0f) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_out_lines) {
|
||||
if (io->a != NULL) {
|
||||
WebPRescaler* const scaler = p->scaler_a;
|
||||
int lines_left = expected_num_out_lines;
|
||||
const int y_end = p->last_y + lines_left;
|
||||
while (lines_left > 0) {
|
||||
const int64_t row_offset = (int64_t)scaler->src_y - io->mb_y;
|
||||
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
|
||||
io->a + row_offset * io->width, io->width);
|
||||
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
const int uv_in_width = (io->mb_w + 1) >> 1;
|
||||
const int uv_in_height = (io->mb_h + 1) >> 1;
|
||||
// scratch memory for one rescaler
|
||||
const size_t work_size = 2 * (size_t)out_width;
|
||||
rescaler_t* work; // rescalers work area
|
||||
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
||||
uint64_t tmp_size1, tmp_size2, total_size;
|
||||
size_t rescaler_size;
|
||||
WebPRescaler* scalers;
|
||||
const int num_rescalers = has_alpha ? 4 : 3;
|
||||
|
||||
tmp_size1 = (uint64_t)num_rescalers * work_size;
|
||||
tmp_size2 = (uint64_t)num_rescalers * out_width;
|
||||
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
|
||||
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||
total_size += rescaler_size;
|
||||
if (!CheckSizeOverflow(total_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)total_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (rescaler_t*)p->memory;
|
||||
tmp = (uint8_t*)(work + tmp_size1);
|
||||
|
||||
scalers = (WebPRescaler*)WEBP_ALIGN(
|
||||
(const uint8_t*)work + total_size - rescaler_size);
|
||||
p->scaler_y = &scalers[0];
|
||||
p->scaler_u = &scalers[1];
|
||||
p->scaler_v = &scalers[2];
|
||||
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||
|
||||
if (!WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||
tmp + 0 * out_width, out_width, out_height, 0, 1,
|
||||
work + 0 * work_size) ||
|
||||
!WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||
tmp + 1 * out_width, out_width, out_height, 0, 1,
|
||||
work + 1 * work_size) ||
|
||||
!WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
|
||||
tmp + 2 * out_width, out_width, out_height, 0, 1,
|
||||
work + 2 * work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit = EmitRescaledRGB;
|
||||
WebPInitYUV444Converters();
|
||||
|
||||
if (has_alpha) {
|
||||
if (!WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||
tmp + 3 * out_width, out_width, out_height, 0, 1,
|
||||
work + 3 * work_size)) {
|
||||
return 0;
|
||||
}
|
||||
p->emit_alpha = EmitRescaledAlphaRGB;
|
||||
if (p->output->colorspace == MODE_RGBA_4444 ||
|
||||
p->output->colorspace == MODE_rgbA_4444) {
|
||||
p->emit_alpha_row = ExportAlphaRGBA4444;
|
||||
} else {
|
||||
p->emit_alpha_row = ExportAlpha;
|
||||
}
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // WEBP_REDUCE_SIZE
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default custom functions
|
||||
|
||||
static int CustomSetup(VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int is_rgb = WebPIsRGBMode(colorspace);
|
||||
const int is_alpha = WebPIsAlphaMode(colorspace);
|
||||
|
||||
p->memory = NULL;
|
||||
p->emit = NULL;
|
||||
p->emit_alpha = NULL;
|
||||
p->emit_alpha_row = NULL;
|
||||
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
|
||||
return 0;
|
||||
}
|
||||
if (is_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPInitUpsamplers();
|
||||
}
|
||||
if (io->use_scaling) {
|
||||
#if !defined(WEBP_REDUCE_SIZE)
|
||||
const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
|
||||
if (!ok) {
|
||||
return 0; // memory error
|
||||
}
|
||||
#else
|
||||
return 0; // rescaling support not compiled
|
||||
#endif
|
||||
} else {
|
||||
if (is_rgb) {
|
||||
WebPInitSamplers();
|
||||
p->emit = EmitSampledRGB; // default
|
||||
if (io->fancy_upsampling) {
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
const int uv_width = (io->mb_w + 1) >> 1;
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width));
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error.
|
||||
}
|
||||
p->tmp_y = (uint8_t*)p->memory;
|
||||
p->tmp_u = p->tmp_y + io->mb_w;
|
||||
p->tmp_v = p->tmp_u + uv_width;
|
||||
p->emit = EmitFancyRGB;
|
||||
WebPInitUpsamplers();
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
p->emit = EmitYUV;
|
||||
}
|
||||
if (is_alpha) { // need transparency output
|
||||
p->emit_alpha =
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
if (is_rgb) {
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int CustomPut(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int num_lines_out;
|
||||
assert(!(io->mb_y & 1));
|
||||
|
||||
if (mb_w <= 0 || mb_h <= 0) {
|
||||
return 0;
|
||||
}
|
||||
num_lines_out = p->emit(io, p);
|
||||
if (p->emit_alpha != NULL) {
|
||||
p->emit_alpha(io, p, num_lines_out);
|
||||
}
|
||||
p->last_y += num_lines_out;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void CustomTeardown(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
WebPSafeFree(p->memory);
|
||||
p->memory = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main entry point
|
||||
|
||||
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
|
||||
io->put = CustomPut;
|
||||
io->setup = CustomSetup;
|
||||
io->teardown = CustomTeardown;
|
||||
io->opaque = params;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Quantizer initialization
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) {
|
||||
return v < 0 ? 0 : v > M ? M : v;
|
||||
}
|
||||
|
||||
// Paragraph 14.1
|
||||
static const uint8_t kDcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 17,
|
||||
18, 19, 20, 20, 21, 21, 22, 22,
|
||||
23, 23, 24, 25, 25, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 64, 65, 66,
|
||||
67, 68, 69, 70, 71, 72, 73, 74,
|
||||
75, 76, 76, 77, 78, 79, 80, 81,
|
||||
82, 83, 84, 85, 86, 87, 88, 89,
|
||||
91, 93, 95, 96, 98, 100, 101, 102,
|
||||
104, 106, 108, 110, 112, 114, 116, 118,
|
||||
122, 124, 126, 128, 130, 132, 134, 136,
|
||||
138, 140, 143, 145, 148, 151, 154, 157
|
||||
};
|
||||
|
||||
static const uint16_t kAcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 60,
|
||||
62, 64, 66, 68, 70, 72, 74, 76,
|
||||
78, 80, 82, 84, 86, 88, 90, 92,
|
||||
94, 96, 98, 100, 102, 104, 106, 108,
|
||||
110, 112, 114, 116, 119, 122, 125, 128,
|
||||
131, 134, 137, 140, 143, 146, 149, 152,
|
||||
155, 158, 161, 164, 167, 170, 173, 177,
|
||||
181, 185, 189, 193, 197, 201, 205, 209,
|
||||
213, 217, 221, 225, 229, 234, 239, 245,
|
||||
249, 254, 259, 264, 269, 274, 279, 284
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 9.6
|
||||
|
||||
void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const int base_q0 = VP8GetValue(br, 7, "global-header");
|
||||
const int dqy1_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
|
||||
const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
|
||||
int q;
|
||||
if (hdr->use_segment_) {
|
||||
q = hdr->quantizer_[i];
|
||||
if (!hdr->absolute_delta_) {
|
||||
q += base_q0;
|
||||
}
|
||||
} else {
|
||||
if (i > 0) {
|
||||
dec->dqm_[i] = dec->dqm_[0];
|
||||
continue;
|
||||
} else {
|
||||
q = base_q0;
|
||||
}
|
||||
}
|
||||
{
|
||||
VP8QuantMatrix* const m = &dec->dqm_[i];
|
||||
m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)];
|
||||
m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
|
||||
|
||||
m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
|
||||
// For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
|
||||
// The smallest precision for that is '(x*6349) >> 12' but 16 is a good
|
||||
// word size.
|
||||
m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
|
||||
if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
|
||||
|
||||
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
|
||||
m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
@ -0,0 +1,538 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Coding trees and probas
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dsp/cpu.h"
|
||||
#include "src/utils/bit_reader_inl_utils.h"
|
||||
|
||||
#if !defined(USE_GENERIC_TREE)
|
||||
#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64
|
||||
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
|
||||
#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
|
||||
#else
|
||||
#define USE_GENERIC_TREE 0
|
||||
#endif
|
||||
#endif // USE_GENERIC_TREE
|
||||
|
||||
#if (USE_GENERIC_TREE == 1)
|
||||
static const int8_t kYModesIntra4[18] = {
|
||||
-B_DC_PRED, 1,
|
||||
-B_TM_PRED, 2,
|
||||
-B_VE_PRED, 3,
|
||||
4, 6,
|
||||
-B_HE_PRED, 5,
|
||||
-B_RD_PRED, -B_VR_PRED,
|
||||
-B_LD_PRED, 7,
|
||||
-B_VL_PRED, 8,
|
||||
-B_HD_PRED, -B_HU_PRED
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Paragraph 13.5
|
||||
static const uint8_t
|
||||
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
|
||||
{ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
|
||||
{ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
|
||||
{ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
|
||||
{ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
|
||||
{ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
|
||||
{ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
|
||||
{ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
|
||||
{ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
|
||||
{ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
|
||||
{ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
|
||||
{ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
|
||||
{ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
|
||||
{ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
|
||||
{ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
|
||||
},
|
||||
{ { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
|
||||
{ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
|
||||
{ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
|
||||
{ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
|
||||
{ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
|
||||
{ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
|
||||
{ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
|
||||
{ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
|
||||
{ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
|
||||
{ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
|
||||
{ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
|
||||
{ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
|
||||
{ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
|
||||
{ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
|
||||
{ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
|
||||
},
|
||||
{ { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
|
||||
{ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
|
||||
{ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
|
||||
{ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
|
||||
{ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
|
||||
{ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
|
||||
{ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
|
||||
{ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
|
||||
},
|
||||
{ { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
|
||||
{ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
|
||||
{ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
|
||||
{ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
|
||||
{ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
|
||||
{ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
|
||||
{ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
|
||||
{ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
|
||||
{ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
|
||||
{ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
|
||||
{ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
|
||||
{ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
|
||||
{ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Paragraph 11.5
|
||||
static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
|
||||
{ { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
|
||||
{ 152, 179, 64, 126, 170, 118, 46, 70, 95 },
|
||||
{ 175, 69, 143, 80, 85, 82, 72, 155, 103 },
|
||||
{ 56, 58, 10, 171, 218, 189, 17, 13, 152 },
|
||||
{ 114, 26, 17, 163, 44, 195, 21, 10, 173 },
|
||||
{ 121, 24, 80, 195, 26, 62, 44, 64, 85 },
|
||||
{ 144, 71, 10, 38, 171, 213, 144, 34, 26 },
|
||||
{ 170, 46, 55, 19, 136, 160, 33, 206, 71 },
|
||||
{ 63, 20, 8, 114, 114, 208, 12, 9, 226 },
|
||||
{ 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
|
||||
{ { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
|
||||
{ 72, 187, 100, 130, 157, 111, 32, 75, 80 },
|
||||
{ 66, 102, 167, 99, 74, 62, 40, 234, 128 },
|
||||
{ 41, 53, 9, 178, 241, 141, 26, 8, 107 },
|
||||
{ 74, 43, 26, 146, 73, 166, 49, 23, 157 },
|
||||
{ 65, 38, 105, 160, 51, 52, 31, 115, 128 },
|
||||
{ 104, 79, 12, 27, 217, 255, 87, 17, 7 },
|
||||
{ 87, 68, 71, 44, 114, 51, 15, 186, 23 },
|
||||
{ 47, 41, 14, 110, 182, 183, 21, 17, 194 },
|
||||
{ 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
|
||||
{ { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
|
||||
{ 43, 97, 183, 117, 85, 38, 35, 179, 61 },
|
||||
{ 39, 53, 200, 87, 26, 21, 43, 232, 171 },
|
||||
{ 56, 34, 51, 104, 114, 102, 29, 93, 77 },
|
||||
{ 39, 28, 85, 171, 58, 165, 90, 98, 64 },
|
||||
{ 34, 22, 116, 206, 23, 34, 43, 166, 73 },
|
||||
{ 107, 54, 32, 26, 51, 1, 81, 43, 31 },
|
||||
{ 68, 25, 106, 22, 64, 171, 36, 225, 114 },
|
||||
{ 34, 19, 21, 102, 132, 188, 16, 76, 124 },
|
||||
{ 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
|
||||
{ { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
|
||||
{ 60, 148, 31, 172, 219, 228, 21, 18, 111 },
|
||||
{ 112, 113, 77, 85, 179, 255, 38, 120, 114 },
|
||||
{ 40, 42, 1, 196, 245, 209, 10, 25, 109 },
|
||||
{ 88, 43, 29, 140, 166, 213, 37, 43, 154 },
|
||||
{ 61, 63, 30, 155, 67, 45, 68, 1, 209 },
|
||||
{ 100, 80, 8, 43, 154, 1, 51, 26, 71 },
|
||||
{ 142, 78, 78, 16, 255, 128, 34, 197, 171 },
|
||||
{ 41, 40, 5, 102, 211, 183, 4, 1, 221 },
|
||||
{ 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
|
||||
{ { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
|
||||
{ 67, 87, 58, 169, 82, 115, 26, 59, 179 },
|
||||
{ 63, 59, 90, 180, 59, 166, 93, 73, 154 },
|
||||
{ 40, 40, 21, 116, 143, 209, 34, 39, 175 },
|
||||
{ 47, 15, 16, 183, 34, 223, 49, 45, 183 },
|
||||
{ 46, 17, 33, 183, 6, 98, 15, 32, 183 },
|
||||
{ 57, 46, 22, 24, 128, 1, 54, 17, 37 },
|
||||
{ 65, 32, 73, 115, 28, 128, 23, 128, 205 },
|
||||
{ 40, 3, 9, 115, 51, 192, 18, 6, 223 },
|
||||
{ 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
|
||||
{ { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
|
||||
{ 64, 90, 70, 205, 40, 41, 23, 26, 57 },
|
||||
{ 54, 57, 112, 184, 5, 41, 38, 166, 213 },
|
||||
{ 30, 34, 26, 133, 152, 116, 10, 32, 134 },
|
||||
{ 39, 19, 53, 221, 26, 114, 32, 73, 255 },
|
||||
{ 31, 9, 65, 234, 2, 15, 1, 118, 73 },
|
||||
{ 75, 32, 12, 51, 192, 255, 160, 43, 51 },
|
||||
{ 88, 31, 35, 67, 102, 85, 55, 186, 85 },
|
||||
{ 56, 21, 23, 111, 59, 205, 45, 37, 192 },
|
||||
{ 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
|
||||
{ { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
|
||||
{ 95, 84, 53, 89, 128, 100, 113, 101, 45 },
|
||||
{ 75, 79, 123, 47, 51, 128, 81, 171, 1 },
|
||||
{ 57, 17, 5, 71, 102, 57, 53, 41, 49 },
|
||||
{ 38, 33, 13, 121, 57, 73, 26, 1, 85 },
|
||||
{ 41, 10, 67, 138, 77, 110, 90, 47, 114 },
|
||||
{ 115, 21, 2, 10, 102, 255, 166, 23, 6 },
|
||||
{ 101, 29, 16, 10, 85, 128, 101, 196, 26 },
|
||||
{ 57, 18, 10, 102, 102, 213, 34, 20, 43 },
|
||||
{ 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
|
||||
{ { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
|
||||
{ 69, 60, 71, 38, 73, 119, 28, 222, 37 },
|
||||
{ 68, 45, 128, 34, 1, 47, 11, 245, 171 },
|
||||
{ 62, 17, 19, 70, 146, 85, 55, 62, 70 },
|
||||
{ 37, 43, 37, 154, 100, 163, 85, 160, 1 },
|
||||
{ 63, 9, 92, 136, 28, 64, 32, 201, 85 },
|
||||
{ 75, 15, 9, 9, 64, 255, 184, 119, 16 },
|
||||
{ 86, 6, 28, 5, 64, 255, 25, 248, 1 },
|
||||
{ 56, 8, 17, 132, 137, 255, 55, 116, 128 },
|
||||
{ 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
|
||||
{ { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
|
||||
{ 51, 103, 44, 131, 131, 123, 31, 6, 158 },
|
||||
{ 86, 40, 64, 135, 148, 224, 45, 183, 128 },
|
||||
{ 22, 26, 17, 131, 240, 154, 14, 1, 209 },
|
||||
{ 45, 16, 21, 91, 64, 222, 7, 1, 197 },
|
||||
{ 56, 21, 39, 155, 60, 138, 23, 102, 213 },
|
||||
{ 83, 12, 13, 54, 192, 255, 68, 47, 28 },
|
||||
{ 85, 26, 85, 85, 128, 128, 32, 146, 171 },
|
||||
{ 18, 11, 7, 63, 144, 171, 4, 4, 246 },
|
||||
{ 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
|
||||
{ { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
|
||||
{ 85, 126, 47, 87, 176, 51, 41, 20, 32 },
|
||||
{ 101, 75, 128, 139, 118, 146, 116, 128, 85 },
|
||||
{ 56, 41, 15, 176, 236, 85, 37, 9, 62 },
|
||||
{ 71, 30, 17, 119, 118, 255, 17, 18, 138 },
|
||||
{ 101, 38, 60, 138, 55, 70, 43, 26, 142 },
|
||||
{ 146, 36, 19, 30, 171, 255, 97, 27, 20 },
|
||||
{ 138, 45, 61, 62, 219, 1, 81, 188, 64 },
|
||||
{ 32, 41, 20, 117, 151, 142, 20, 21, 163 },
|
||||
{ 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
|
||||
};
|
||||
|
||||
void VP8ResetProba(VP8Proba* const proba) {
|
||||
memset(proba->segments_, 255u, sizeof(proba->segments_));
|
||||
// proba->bands_[][] is initialized later
|
||||
}
|
||||
|
||||
static void ParseIntraMode(VP8BitReader* const br,
|
||||
VP8Decoder* const dec, int mb_x) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * mb_x;
|
||||
uint8_t* const left = dec->intra_l_;
|
||||
VP8MBData* const block = dec->mb_data_ + mb_x;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0], "segments")
|
||||
? VP8GetBit(br, dec->proba_.segments_[1], "segments")
|
||||
: VP8GetBit(br, dec->proba_.segments_[2], "segments") + 2;
|
||||
} else {
|
||||
block->segment_ = 0; // default for intra
|
||||
}
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip");
|
||||
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145, "block-size");
|
||||
if (!block->is_i4x4_) {
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156, "pred-modes") ?
|
||||
(VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED) :
|
||||
(VP8GetBit(br, 163, "pred-modes") ? V_PRED : DC_PRED);
|
||||
block->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(*top));
|
||||
memset(left, ymode, 4 * sizeof(*left));
|
||||
} else {
|
||||
uint8_t* modes = block->imodes_;
|
||||
int y;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int ymode = left[y];
|
||||
int x;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const uint8_t* const prob = kBModesProba[top[x]][ymode];
|
||||
#if (USE_GENERIC_TREE == 1)
|
||||
// Generic tree-parsing
|
||||
int i = kYModesIntra4[VP8GetBit(br, prob[0], "pred-modes")];
|
||||
while (i > 0) {
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i], "pred-modes")];
|
||||
}
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
ymode = !VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED :
|
||||
!VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED :
|
||||
!VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED :
|
||||
!VP8GetBit(br, prob[3], "pred-modes") ?
|
||||
(!VP8GetBit(br, prob[4], "pred-modes") ? B_HE_PRED :
|
||||
(!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED
|
||||
: B_VR_PRED)) :
|
||||
(!VP8GetBit(br, prob[6], "pred-modes") ? B_LD_PRED :
|
||||
(!VP8GetBit(br, prob[7], "pred-modes") ? B_VL_PRED :
|
||||
(!VP8GetBit(br, prob[8], "pred-modes") ? B_HD_PRED
|
||||
: B_HU_PRED))
|
||||
);
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
}
|
||||
memcpy(modes, top, 4 * sizeof(*top));
|
||||
modes += 4;
|
||||
left[y] = ymode;
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
block->uvmode_ = !VP8GetBit(br, 142, "pred-modes-uv") ? DC_PRED
|
||||
: !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED
|
||||
: VP8GetBit(br, 183, "pred-modes-uv") ? TM_PRED : H_PRED;
|
||||
}
|
||||
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
ParseIntraMode(br, dec, mb_x);
|
||||
}
|
||||
return !dec->br_.eof_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 13
|
||||
|
||||
static const uint8_t
|
||||
CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
|
||||
{ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Paragraph 9.9
|
||||
|
||||
static const uint8_t kBands[16 + 1] = {
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
};
|
||||
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
VP8Proba* const proba = &dec->proba_;
|
||||
int t, b, c, p;
|
||||
for (t = 0; t < NUM_TYPES; ++t) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
const int v =
|
||||
VP8GetBit(br, CoeffsUpdateProba[t][b][c][p], "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") :
|
||||
CoeffsProba0[t][b][c][p];
|
||||
proba->bands_[t][b].probas_[c][p] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (b = 0; b < 16 + 1; ++b) {
|
||||
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br, "global-header");
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8, "global-header");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,724 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// main entry for the decoder
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "src/dec/alphai_dec.h"
|
||||
#include "src/dec/vp8i_dec.h"
|
||||
#include "src/dec/vp8li_dec.h"
|
||||
#include "src/dec/webpi_dec.h"
|
||||
#include "src/utils/bit_reader_inl_utils.h"
|
||||
#include "src/utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPGetDecoderVersion(void) {
|
||||
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Signature and pointer-to-function for GetCoeffs() variants below.
|
||||
|
||||
typedef int (*GetCoeffsFunc)(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out);
|
||||
static volatile GetCoeffsFunc GetCoeffs = NULL;
|
||||
|
||||
static void InitGetCoeffs(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder
|
||||
|
||||
static void SetOk(VP8Decoder* const dec) {
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
dec->error_msg_ = "OK";
|
||||
}
|
||||
|
||||
int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return 0; // mismatch error
|
||||
}
|
||||
if (io != NULL) {
|
||||
memset(io, 0, sizeof(*io));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
VP8Decoder* VP8New(void) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
if (dec != NULL) {
|
||||
SetOk(dec);
|
||||
WebPGetWorkerInterface()->Init(&dec->worker_);
|
||||
dec->ready_ = 0;
|
||||
dec->num_parts_minus_one_ = 0;
|
||||
InitGetCoeffs();
|
||||
}
|
||||
return dec;
|
||||
}
|
||||
|
||||
VP8StatusCode VP8Status(VP8Decoder* const dec) {
|
||||
if (!dec) return VP8_STATUS_INVALID_PARAM;
|
||||
return dec->status_;
|
||||
}
|
||||
|
||||
const char* VP8StatusMessage(VP8Decoder* const dec) {
|
||||
if (dec == NULL) return "no object";
|
||||
if (!dec->error_msg_) return "OK";
|
||||
return dec->error_msg_;
|
||||
}
|
||||
|
||||
void VP8Delete(VP8Decoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8Clear(dec);
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
dec->error_msg_ = msg;
|
||||
dec->ready_ = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
|
||||
return (data_size >= 3 &&
|
||||
data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
|
||||
}
|
||||
|
||||
int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
int* const width, int* const height) {
|
||||
if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return 0; // not enough data
|
||||
}
|
||||
// check signature
|
||||
if (!VP8CheckSignature(data + 3, data_size - 3)) {
|
||||
return 0; // Wrong signature.
|
||||
} else {
|
||||
const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
const int key_frame = !(bits & 1);
|
||||
const int w = ((data[7] << 8) | data[6]) & 0x3fff;
|
||||
const int h = ((data[9] << 8) | data[8]) & 0x3fff;
|
||||
|
||||
if (!key_frame) { // Not a keyframe.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((bits >> 1) & 7) > 3) {
|
||||
return 0; // unknown profile
|
||||
}
|
||||
if (!((bits >> 4) & 1)) {
|
||||
return 0; // first frame is invisible!
|
||||
}
|
||||
if (((bits >> 5)) >= chunk_size) { // partition_length
|
||||
return 0; // inconsistent size information.
|
||||
}
|
||||
if (w == 0 || h == 0) {
|
||||
return 0; // We don't support both width and height to be zero.
|
||||
}
|
||||
|
||||
if (width) {
|
||||
*width = w;
|
||||
}
|
||||
if (height) {
|
||||
*height = h;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Header parsing
|
||||
|
||||
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = 0;
|
||||
hdr->update_map_ = 0;
|
||||
hdr->absolute_delta_ = 1;
|
||||
memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
|
||||
memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
|
||||
}
|
||||
|
||||
// Paragraph 9.3
|
||||
static int ParseSegmentHeader(VP8BitReader* br,
|
||||
VP8SegmentHeader* hdr, VP8Proba* proba) {
|
||||
assert(br != NULL);
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_segment_) {
|
||||
hdr->update_map_ = VP8Get(br, "global-header");
|
||||
if (VP8Get(br, "global-header")) { // update data
|
||||
int s;
|
||||
hdr->absolute_delta_ = VP8Get(br, "global-header");
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->quantizer_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 7, "global-header") : 0;
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->filter_strength_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 6, "global-header") : 0;
|
||||
}
|
||||
}
|
||||
if (hdr->update_map_) {
|
||||
int s;
|
||||
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
||||
proba->segments_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") : 255u;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hdr->update_map_ = 0;
|
||||
}
|
||||
return !br->eof_;
|
||||
}
|
||||
|
||||
// Paragraph 9.5
|
||||
// This function returns VP8_STATUS_SUSPENDED if we don't have all the
|
||||
// necessary data in 'buf'.
|
||||
// This case is not necessarily an error (for incremental decoding).
|
||||
// Still, no bitreader is ever initialized to make it possible to read
|
||||
// unavailable memory.
|
||||
// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
|
||||
// is returned, and this is an unrecoverable error.
|
||||
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
|
||||
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
const uint8_t* buf, size_t size) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const uint8_t* sz = buf;
|
||||
const uint8_t* buf_end = buf + size;
|
||||
const uint8_t* part_start;
|
||||
size_t size_left = size;
|
||||
size_t last_part;
|
||||
size_t p;
|
||||
|
||||
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2, "global-header")) - 1;
|
||||
last_part = dec->num_parts_minus_one_;
|
||||
if (size < 3 * last_part) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
part_start = buf + last_part * 3;
|
||||
size_left -= last_part * 3;
|
||||
for (p = 0; p < last_part; ++p) {
|
||||
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
if (psize > size_left) psize = size_left;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
||||
part_start += psize;
|
||||
size_left -= psize;
|
||||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||
}
|
||||
|
||||
// Paragraph 9.4
|
||||
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
||||
VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
hdr->simple_ = VP8Get(br, "global-header");
|
||||
hdr->level_ = VP8GetValue(br, 6, "global-header");
|
||||
hdr->sharpness_ = VP8GetValue(br, 3, "global-header");
|
||||
hdr->use_lf_delta_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_lf_delta_) {
|
||||
if (VP8Get(br, "global-header")) { // update lf-delta?
|
||||
int i;
|
||||
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
||||
return !br->eof_;
|
||||
}
|
||||
|
||||
// Topmost call
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
const uint8_t* buf;
|
||||
size_t buf_size;
|
||||
VP8FrameHeader* frm_hdr;
|
||||
VP8PictureHeader* pic_hdr;
|
||||
VP8BitReader* br;
|
||||
VP8StatusCode status;
|
||||
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
}
|
||||
SetOk(dec);
|
||||
if (io == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"null VP8Io passed to VP8GetHeaders()");
|
||||
}
|
||||
buf = io->data;
|
||||
buf_size = io->data_size;
|
||||
if (buf_size < 4) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Truncated header.");
|
||||
}
|
||||
|
||||
// Paragraph 9.1
|
||||
{
|
||||
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
frm_hdr = &dec->frm_hdr_;
|
||||
frm_hdr->key_frame_ = !(bits & 1);
|
||||
frm_hdr->profile_ = (bits >> 1) & 7;
|
||||
frm_hdr->show_ = (bits >> 4) & 1;
|
||||
frm_hdr->partition_length_ = (bits >> 5);
|
||||
if (frm_hdr->profile_ > 3) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Incorrect keyframe parameters.");
|
||||
}
|
||||
if (!frm_hdr->show_) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Frame not displayable.");
|
||||
}
|
||||
buf += 3;
|
||||
buf_size -= 3;
|
||||
}
|
||||
|
||||
pic_hdr = &dec->pic_hdr_;
|
||||
if (frm_hdr->key_frame_) {
|
||||
// Paragraph 9.2
|
||||
if (buf_size < 7) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"cannot parse picture header");
|
||||
}
|
||||
if (!VP8CheckSignature(buf, buf_size)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Bad code word");
|
||||
}
|
||||
pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
|
||||
pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
|
||||
pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
|
||||
pic_hdr->yscale_ = buf[6] >> 6;
|
||||
buf += 7;
|
||||
buf_size -= 7;
|
||||
|
||||
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
||||
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
||||
|
||||
// Setup default output area (can be later modified during io->setup())
|
||||
io->width = pic_hdr->width_;
|
||||
io->height = pic_hdr->height_;
|
||||
// IMPORTANT! use some sane dimensions in crop_* and scaled_* fields.
|
||||
// So they can be used interchangeably without always testing for
|
||||
// 'use_cropping'.
|
||||
io->use_cropping = 0;
|
||||
io->crop_top = 0;
|
||||
io->crop_left = 0;
|
||||
io->crop_right = io->width;
|
||||
io->crop_bottom = io->height;
|
||||
io->use_scaling = 0;
|
||||
io->scaled_width = io->width;
|
||||
io->scaled_height = io->height;
|
||||
|
||||
io->mb_w = io->width; // for soundness
|
||||
io->mb_h = io->height; // ditto
|
||||
|
||||
VP8ResetProba(&dec->proba_);
|
||||
ResetSegmentHeader(&dec->segment_hdr_);
|
||||
}
|
||||
|
||||
// Check if we have all the partition #0 available, and initialize dec->br_
|
||||
// to read this partition (and this partition only).
|
||||
if (frm_hdr->partition_length_ > buf_size) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"bad partition length");
|
||||
}
|
||||
|
||||
br = &dec->br_;
|
||||
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
||||
buf += frm_hdr->partition_length_;
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
|
||||
if (frm_hdr->key_frame_) {
|
||||
pic_hdr->colorspace_ = VP8Get(br, "global-header");
|
||||
pic_hdr->clamp_type_ = VP8Get(br, "global-header");
|
||||
}
|
||||
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"cannot parse segment header");
|
||||
}
|
||||
// Filter specs
|
||||
if (!ParseFilterHeader(br, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"cannot parse filter header");
|
||||
}
|
||||
status = ParsePartitions(dec, buf, buf_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return VP8SetError(dec, status, "cannot parse partitions");
|
||||
}
|
||||
|
||||
// quantizer change
|
||||
VP8ParseQuant(dec);
|
||||
|
||||
// Frame buffer marking
|
||||
if (!frm_hdr->key_frame_) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Not a key frame.");
|
||||
}
|
||||
|
||||
VP8Get(br, "global-header"); // ignore the value of update_proba_
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
// sanitized state
|
||||
dec->ready_ = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Residual decoding (Paragraph 13.2 / 13.3)
|
||||
|
||||
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
||||
static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
|
||||
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
||||
static const uint8_t kCat6[] =
|
||||
{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
|
||||
static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
|
||||
static const uint8_t kZigzag[16] = {
|
||||
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
||||
};
|
||||
|
||||
// See section 13-2: https://datatracker.ietf.org/doc/html/rfc6386#section-13.2
|
||||
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[3], "coeffs")) {
|
||||
if (!VP8GetBit(br, p[4], "coeffs")) {
|
||||
v = 2;
|
||||
} else {
|
||||
v = 3 + VP8GetBit(br, p[5], "coeffs");
|
||||
}
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[6], "coeffs")) {
|
||||
if (!VP8GetBit(br, p[7], "coeffs")) {
|
||||
v = 5 + VP8GetBit(br, 159, "coeffs");
|
||||
} else {
|
||||
v = 7 + 2 * VP8GetBit(br, 165, "coeffs");
|
||||
v += VP8GetBit(br, 145, "coeffs");
|
||||
}
|
||||
} else {
|
||||
const uint8_t* tab;
|
||||
const int bit1 = VP8GetBit(br, p[8], "coeffs");
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1], "coeffs");
|
||||
const int cat = 2 * bit1 + bit0;
|
||||
v = 0;
|
||||
for (tab = kCat3456[cat]; *tab; ++tab) {
|
||||
v += v + VP8GetBit(br, *tab, "coeffs");
|
||||
}
|
||||
v += 3 + (8 << cat);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
static int GetCoeffsFast(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
if (!VP8GetBit(br, p[0], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBit(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
if (n == 16) return 16;
|
||||
}
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
p = p_ctx[1];
|
||||
} else {
|
||||
v = GetLargeValue(br, p);
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
// This version of GetCoeffs() uses VP8GetBitAlt() which is an alternate version
|
||||
// of VP8GetBitAlt() targeting specific platforms.
|
||||
static int GetCoeffsAlt(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
if (!VP8GetBitAlt(br, p[0], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBitAlt(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
if (n == 16) return 16;
|
||||
}
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||
int v;
|
||||
if (!VP8GetBitAlt(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
p = p_ctx[1];
|
||||
} else {
|
||||
v = GetLargeValue(br, p);
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
extern VP8CPUInfo VP8GetCPUInfo;
|
||||
|
||||
WEBP_DSP_INIT_FUNC(InitGetCoeffs) {
|
||||
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
|
||||
GetCoeffs = GetCoeffsAlt;
|
||||
} else {
|
||||
GetCoeffs = GetCoeffsFast;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
||||
nz_coeffs <<= 2;
|
||||
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
||||
return nz_coeffs;
|
||||
}
|
||||
|
||||
static int ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
||||
const VP8BandProbas* const * ac_proba;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
||||
int16_t* dst = block->coeffs_;
|
||||
VP8MB* const left_mb = dec->mb_info_ - 1;
|
||||
uint8_t tnz, lnz;
|
||||
uint32_t non_zero_y = 0;
|
||||
uint32_t non_zero_uv = 0;
|
||||
int x, y, ch;
|
||||
uint32_t out_t_nz, out_l_nz;
|
||||
int first;
|
||||
|
||||
memset(dst, 0, 384 * sizeof(*dst));
|
||||
if (!block->is_i4x4_) { // parse DC
|
||||
int16_t dc[16] = { 0 };
|
||||
const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
|
||||
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
|
||||
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
|
||||
if (nz > 1) { // more than just the DC -> perform the full transform
|
||||
VP8TransformWHT(dc, dst);
|
||||
} else { // only DC is non-zero -> inlined simplified transform
|
||||
int i;
|
||||
const int dc0 = (dc[0] + 3) >> 3;
|
||||
for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
|
||||
}
|
||||
first = 1;
|
||||
ac_proba = bands[0];
|
||||
} else {
|
||||
first = 0;
|
||||
ac_proba = bands[3];
|
||||
}
|
||||
|
||||
tnz = mb->nz_ & 0x0f;
|
||||
lnz = left_mb->nz_ & 0x0f;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int l = lnz & 1;
|
||||
uint32_t nz_coeffs = 0;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
|
||||
l = (nz > first);
|
||||
tnz = (tnz >> 1) | (l << 7);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
tnz >>= 4;
|
||||
lnz = (lnz >> 1) | (l << 7);
|
||||
non_zero_y = (non_zero_y << 8) | nz_coeffs;
|
||||
}
|
||||
out_t_nz = tnz;
|
||||
out_l_nz = lnz >> 4;
|
||||
|
||||
for (ch = 0; ch < 4; ch += 2) {
|
||||
uint32_t nz_coeffs = 0;
|
||||
tnz = mb->nz_ >> (4 + ch);
|
||||
lnz = left_mb->nz_ >> (4 + ch);
|
||||
for (y = 0; y < 2; ++y) {
|
||||
int l = lnz & 1;
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
|
||||
l = (nz > 0);
|
||||
tnz = (tnz >> 1) | (l << 3);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
tnz >>= 2;
|
||||
lnz = (lnz >> 1) | (l << 5);
|
||||
}
|
||||
// Note: we don't really need the per-4x4 details for U/V blocks.
|
||||
non_zero_uv |= nz_coeffs << (4 * ch);
|
||||
out_t_nz |= (tnz << 4) << ch;
|
||||
out_l_nz |= (lnz & 0xf0) << ch;
|
||||
}
|
||||
mb->nz_ = out_t_nz;
|
||||
left_mb->nz_ = out_l_nz;
|
||||
|
||||
block->non_zero_y_ = non_zero_y;
|
||||
block->non_zero_uv_ = non_zero_uv;
|
||||
|
||||
// We look at the mode-code of each block and check if some blocks have less
|
||||
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
|
||||
// empty blocks.
|
||||
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
|
||||
|
||||
return !(non_zero_y | non_zero_uv); // will be used for further optimization
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main loop
|
||||
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
|
||||
|
||||
if (!skip) {
|
||||
skip = ParseResiduals(dec, mb, token_br);
|
||||
} else {
|
||||
left->nz_ = mb->nz_ = 0;
|
||||
if (!block->is_i4x4_) {
|
||||
left->nz_dc_ = mb->nz_dc_ = 0;
|
||||
}
|
||||
block->non_zero_y_ = 0;
|
||||
block->non_zero_uv_ = 0;
|
||||
block->dither_ = 0;
|
||||
}
|
||||
|
||||
if (dec->filter_type_ > 0) { // store filter info
|
||||
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
|
||||
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
|
||||
finfo->f_inner_ |= !skip;
|
||||
}
|
||||
|
||||
return !token_br->eof_;
|
||||
}
|
||||
|
||||
void VP8InitScanline(VP8Decoder* const dec) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
left->nz_ = 0;
|
||||
left->nz_dc_ = 0;
|
||||
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
|
||||
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||
// Parse bitstream for this row.
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-partition0 encountered.");
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-file encountered.");
|
||||
}
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
||||
}
|
||||
}
|
||||
if (dec->mt_method_ > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Main entry point
|
||||
int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 0;
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (io == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"NULL VP8Io parameter in VP8Decode().");
|
||||
}
|
||||
|
||||
if (!dec->ready_) {
|
||||
if (!VP8GetHeaders(dec, io)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
assert(dec->ready_);
|
||||
|
||||
// Finish setting up the decoding parameter. Will call io->setup().
|
||||
ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
|
||||
if (ok) { // good to go.
|
||||
// Will allocate memory and prepare everything.
|
||||
if (ok) ok = VP8InitFrame(dec, io);
|
||||
|
||||
// Main decoding loop
|
||||
if (ok) ok = ParseFrame(dec, io);
|
||||
|
||||
// Exit.
|
||||
ok &= VP8ExitCritical(dec, io);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
VP8Clear(dec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dec->ready_ = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void VP8Clear(VP8Decoder* const dec) {
|
||||
if (dec == NULL) {
|
||||
return;
|
||||
}
|
||||
WebPGetWorkerInterface()->End(&dec->worker_);
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_ = NULL;
|
||||
dec->mem_size_ = 0;
|
||||
memset(&dec->br_, 0, sizeof(dec->br_));
|
||||
dec->ready_ = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue