Compare commits
1341 Commits
v0.4.3-rc1
...
sandbox/jz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86c756929c | ||
|
|
50d1a848bc | ||
|
|
20a7fea064 | ||
|
|
415f3ffe3d | ||
|
|
3c6d1224b4 | ||
|
|
ee4a4141f5 | ||
|
|
32ed856f60 | ||
|
|
f4dc56fd77 | ||
|
|
0d8e05880c | ||
|
|
b045013970 | ||
|
|
0ad3b4efa8 | ||
|
|
6451709e72 | ||
|
|
92504d214c | ||
|
|
c3e4b3a946 | ||
|
|
3363eb6d68 | ||
|
|
4d1312f2d5 | ||
|
|
36c42ea415 | ||
|
|
bb498a51e2 | ||
|
|
84cef16f47 | ||
|
|
919f9e2fd6 | ||
|
|
f1ae8af42f | ||
|
|
4689ce1635 | ||
|
|
79bf46f120 | ||
|
|
eb1dc89a5f | ||
|
|
c85b0dde6e | ||
|
|
43d3f01a2f | ||
|
|
668e1dd44f | ||
|
|
0e6b7f33ab | ||
|
|
29ed6f9a0b | ||
|
|
71c53f1aeb | ||
|
|
73f567ea8b | ||
|
|
9e14276f4c | ||
|
|
99965bac02 | ||
|
|
d4912238f7 | ||
|
|
ea4824097e | ||
|
|
2ede5a198a | ||
|
|
a345068aba | ||
|
|
1dc82a6bba | ||
|
|
8074b89eb3 | ||
|
|
749a45a520 | ||
|
|
74c053b57d | ||
|
|
0a3aeff75b | ||
|
|
1de931c669 | ||
|
|
9b3aca404d | ||
|
|
1c07a3c639 | ||
|
|
9ed5e3e5dd | ||
|
|
db013a8d5c | ||
|
|
fcd4784dcd | ||
|
|
fbb5c473b4 | ||
|
|
8fda56126e | ||
|
|
86bbd24552 | ||
|
|
7c2779e95a | ||
|
|
250c358662 | ||
|
|
c0648ac2ae | ||
|
|
0d55f60c91 | ||
|
|
49d0280df1 | ||
|
|
48b1e85fbe | ||
|
|
e3b8abbc9b | ||
|
|
28fe054e73 | ||
|
|
f44acd253b | ||
|
|
527844fee0 | ||
|
|
be0ef6395f | ||
|
|
8874b16275 | ||
|
|
d712e20de0 | ||
|
|
ecff04f625 | ||
|
|
259e98286a | ||
|
|
00b08c88c0 | ||
|
|
0e7f444702 | ||
|
|
b016cb91c5 | ||
|
|
1cb638010c | ||
|
|
875fafc191 | ||
|
|
3674d49e63 | ||
|
|
f04eb37603 | ||
|
|
341d711c43 | ||
|
|
abb5482731 | ||
|
|
ece9684f52 | ||
|
|
aa7744ca38 | ||
|
|
d9120271e7 | ||
|
|
24eb39401b | ||
|
|
a4a8e5f32c | ||
|
|
67c25ad5b4 | ||
|
|
a4bbe4b38b | ||
|
|
31ca2a8098 | ||
|
|
b2f77b57a9 | ||
|
|
5ab6d9de1f | ||
|
|
f29bf582df | ||
|
|
3ebe1c0003 | ||
|
|
df780e0eac | ||
|
|
58fc507842 | ||
|
|
9cc421675b | ||
|
|
0aa1f35c0f | ||
|
|
cb9ec84b54 | ||
|
|
dc0c01fbd3 | ||
|
|
827d3c5038 | ||
|
|
1e2e25b0d4 | ||
|
|
218460cdd7 | ||
|
|
de7d654d0d | ||
|
|
273367c130 | ||
|
|
76bbcf2ed6 | ||
|
|
8ac1abfe13 | ||
|
|
cb215aed5c | ||
|
|
2423017a28 | ||
|
|
74a12b10d9 | ||
|
|
a9cc762196 | ||
|
|
fbba5bc2c1 | ||
|
|
9ae0b3f65a | ||
|
|
c1f97bd758 | ||
|
|
ea664b8995 | ||
|
|
be7dcc088c | ||
|
|
408858308a | ||
|
|
1d5046d1f9 | ||
|
|
cec7201447 | ||
|
|
8f38c72e11 | ||
|
|
33ca93f909 | ||
|
|
76e190735b | ||
|
|
f91ba96306 | ||
|
|
25d74e652e | ||
|
|
03f1c00877 | ||
|
|
58410cd6dc | ||
|
|
e168af8c6c | ||
|
|
ed9dec41a5 | ||
|
|
3c49178f7d | ||
|
|
9595f29010 | ||
|
|
7ec9552c22 | ||
|
|
b3fb8bb602 | ||
|
|
9871335fc8 | ||
|
|
0ae32226ce | ||
|
|
ab4c8056e0 | ||
|
|
eec5fa3a95 | ||
|
|
004d569086 | ||
|
|
4fe5d588bf | ||
|
|
bd63a31aab | ||
|
|
363a568131 | ||
|
|
a0d2753fcb | ||
|
|
31fe11a57a | ||
|
|
532215dd29 | ||
|
|
9c75dbd39c | ||
|
|
af2e05cbdf | ||
|
|
26ffa2962b | ||
|
|
7416280d75 | ||
|
|
13cf1d2e41 | ||
|
|
eb9a4b97c5 | ||
|
|
42ebe3b783 | ||
|
|
76ebbfff28 | ||
|
|
95b12a08ae | ||
|
|
54ab2e758f | ||
|
|
fe12330c81 | ||
|
|
fbfb3bef7b | ||
|
|
d4b7d801db | ||
|
|
a5e3b22574 | ||
|
|
58a1f124c2 | ||
|
|
906c3b6392 | ||
|
|
d23abe4e9f | ||
|
|
2e6cb6f34e | ||
|
|
28e0bb7088 | ||
|
|
647045305a | ||
|
|
bded7848ea | ||
|
|
0e61a5134a | ||
|
|
1cc79e92ac | ||
|
|
03f40955a3 | ||
|
|
ea72cd60cb | ||
|
|
67879e6d48 | ||
|
|
34aee99026 | ||
|
|
a41296aef5 | ||
|
|
c85adb33d2 | ||
|
|
4239a1489c | ||
|
|
bc18ebad2e | ||
|
|
71e2f5cadf | ||
|
|
7474d46e45 | ||
|
|
67748b41db | ||
|
|
16951b1905 | ||
|
|
6540cd0eeb | ||
|
|
de568abfdb | ||
|
|
3c2a61b099 | ||
|
|
9ac063c37f | ||
|
|
22efabddb4 | ||
|
|
1d6e7bf39f | ||
|
|
0a3838ca77 | ||
|
|
c0699515af | ||
|
|
83cbfa09a1 | ||
|
|
a1ade40ed8 | ||
|
|
fd4d090fd1 | ||
|
|
9daad4598b | ||
|
|
f90c60d129 | ||
|
|
3f182d36f4 | ||
|
|
342e15f0ce | ||
|
|
1147ab4ee7 | ||
|
|
e4cd4daf74 | ||
|
|
e715285611 | ||
|
|
de9fa5074e | ||
|
|
deb1b83199 | ||
|
|
c284780f0a | ||
|
|
e375080d8f | ||
|
|
c222a053af | ||
|
|
bb2336170b | ||
|
|
883d41fb40 | ||
|
|
cac9a36a23 | ||
|
|
4595e01fd0 | ||
|
|
fb52d4432a | ||
|
|
f048d38d38 | ||
|
|
31b1e34342 | ||
|
|
2f51b614b0 | ||
|
|
0104d730bf | ||
|
|
265abbe9b2 | ||
|
|
f7601aa686 | ||
|
|
ce8733209d | ||
|
|
2a2773ea29 | ||
|
|
9f5c8eca79 | ||
|
|
4eb5df28d1 | ||
|
|
11bc423ae5 | ||
|
|
273d035a44 | ||
|
|
4db82a178f | ||
|
|
e2affacc35 | ||
|
|
dc789ada44 | ||
|
|
539f5a688f | ||
|
|
aaf2a6a698 | ||
|
|
20ef99159e | ||
|
|
bc86b7a8a1 | ||
|
|
806f6279ae | ||
|
|
68ae5b671f | ||
|
|
28ce304344 | ||
|
|
b34abcb8b1 | ||
|
|
ba843a92e7 | ||
|
|
51b71fd295 | ||
|
|
fb01743a53 | ||
|
|
bfab894739 | ||
|
|
9310d19258 | ||
|
|
f79450ca02 | ||
|
|
cfdda7c6bf | ||
|
|
e36396ba75 | ||
|
|
18a9a0ab57 | ||
|
|
30d43706d3 | ||
|
|
36aa087b6d | ||
|
|
86a84b3598 | ||
|
|
b8384b53d6 | ||
|
|
1c36440094 | ||
|
|
eee0cce158 | ||
|
|
5f1caf2987 | ||
|
|
1effde7bcd | ||
|
|
a2fe9bf404 | ||
|
|
573cce270e | ||
|
|
21e7537abe | ||
|
|
053a1565c8 | ||
|
|
1377ac2ec1 | ||
|
|
7c1fb7d0ff | ||
|
|
bfff0bf329 | ||
|
|
64577de8ae | ||
|
|
a7be73280b | ||
|
|
50c3d7da9a | ||
|
|
d6228aed6a | ||
|
|
dd538b192d | ||
|
|
6cc48b1728 | ||
|
|
78363e9e51 | ||
|
|
ffd01929f1 | ||
|
|
c4f6d9c939 | ||
|
|
c27d821096 | ||
|
|
0779529616 | ||
|
|
c9b45863e2 | ||
|
|
490ae5b13d | ||
|
|
894232be56 | ||
|
|
8de08483b6 | ||
|
|
41cab7fe0f | ||
|
|
82c91c708e | ||
|
|
af1ad3e2dd | ||
|
|
dd7309e30c | ||
|
|
6105777e8c | ||
|
|
13ae011e4c | ||
|
|
4bda0cfba4 | ||
|
|
6029c7fe68 | ||
|
|
545c147fd8 | ||
|
|
5b46f7fc80 | ||
|
|
9e478f808e | ||
|
|
43bd895879 | ||
|
|
0887fc2d85 | ||
|
|
0de48e187d | ||
|
|
0a57ad0dc5 | ||
|
|
0a4699bcc9 | ||
|
|
29fedbf58b | ||
|
|
32dead4ee3 | ||
|
|
85cd5d061c | ||
|
|
6585075f8a | ||
|
|
c0a27fd2af | ||
|
|
57a5e3b6a5 | ||
|
|
7f1b897bee | ||
|
|
48c810b85c | ||
|
|
3884972e3f | ||
|
|
485cac1a88 | ||
|
|
005e15b10a | ||
|
|
6ab496ed22 | ||
|
|
8a4ebc6ab0 | ||
|
|
9d4f209f80 | ||
|
|
e44f5248ff | ||
|
|
27b5d991e2 | ||
|
|
74f6f9e793 | ||
|
|
aaf2530cc3 | ||
|
|
1269dc7cfb | ||
|
|
40872fb2e6 | ||
|
|
7b54e26bac | ||
|
|
d2223d8d6c | ||
|
|
bf16a4b4f8 | ||
|
|
ee1057e3b1 | ||
|
|
b551e587b3 | ||
|
|
d2e4484ef3 | ||
|
|
c7f66c82b3 | ||
|
|
98d8f295b1 | ||
|
|
39f4ffbcdc | ||
|
|
a86ce2b1e2 | ||
|
|
6fa9fe2482 | ||
|
|
0b2c58a91c | ||
|
|
d7ce4a2e3e | ||
|
|
386e4ba2f0 | ||
|
|
7b87e8485b | ||
|
|
d3ddacb625 | ||
|
|
eb98d8d87c | ||
|
|
ebee57f4d1 | ||
|
|
99542bbf3e | ||
|
|
da573cf490 | ||
|
|
bdda5bd4bf | ||
|
|
15ed462b2d | ||
|
|
7be5748963 | ||
|
|
57020525ed | ||
|
|
e8ab6a825a | ||
|
|
fee7b3d6ca | ||
|
|
1e7d4401dc | ||
|
|
4cecab63bd | ||
|
|
259f0434a5 | ||
|
|
ed34c39bfc | ||
|
|
33d8d0d4d3 | ||
|
|
c960b82e5c | ||
|
|
fe3cd28a53 | ||
|
|
45fbeba5cd | ||
|
|
4f33c82098 | ||
|
|
c379b55a93 | ||
|
|
5108d9aa00 | ||
|
|
ad497fbce3 | ||
|
|
0c0fb83211 | ||
|
|
9ac74f922e | ||
|
|
cb19dbc1a4 | ||
|
|
3f4042b52a | ||
|
|
5e2eb89e1f | ||
|
|
5b60db5c9d | ||
|
|
567e697776 | ||
|
|
c54ab8dd1a | ||
|
|
ec6f68c51e | ||
|
|
2a5c417c68 | ||
|
|
91b59e886b | ||
|
|
fe57273736 | ||
|
|
6b53ca876e | ||
|
|
b15d00d996 | ||
|
|
afe3cec813 | ||
|
|
fc8cad9f29 | ||
|
|
7b4b05e0dc | ||
|
|
c18787a0e9 | ||
|
|
479d19085d | ||
|
|
a80e8cfdf2 | ||
|
|
6c62841076 | ||
|
|
bbb6ecd9b0 | ||
|
|
7915396f40 | ||
|
|
652e944f69 | ||
|
|
c0991a141f | ||
|
|
48bf5ed134 | ||
|
|
bfef6c9f82 | ||
|
|
3d97bb7514 | ||
|
|
deb54d915a | ||
|
|
435308e029 | ||
|
|
dce64bfa1b | ||
|
|
429120d0af | ||
|
|
c7e2d245d1 | ||
|
|
55b2fede7f | ||
|
|
701c772eed | ||
|
|
c7eb06f737 | ||
|
|
f918cb1013 | ||
|
|
ab7937a5c7 | ||
|
|
3cdec847a5 | ||
|
|
bdf6241e70 | ||
|
|
06a38c7b1c | ||
|
|
319e37be13 | ||
|
|
6a19793777 | ||
|
|
447adbce1e | ||
|
|
97b9e64459 | ||
|
|
293d786f31 | ||
|
|
0afa0ce2ff | ||
|
|
a6621bacf3 | ||
|
|
bb50bf42b0 | ||
|
|
38063af131 | ||
|
|
1ebf193c2c | ||
|
|
9ad2352d0d | ||
|
|
607510967f | ||
|
|
9e8e1b7b2a | ||
|
|
7d58d1b7b9 | ||
|
|
8ec7032bc2 | ||
|
|
23e29cb1e3 | ||
|
|
472a049b4e | ||
|
|
0bb23b2cf7 | ||
|
|
a977b4b513 | ||
|
|
3591ba6684 | ||
|
|
e6ac450cbd | ||
|
|
e7b917726f | ||
|
|
2abfa54f95 | ||
|
|
5a48fcd8a1 | ||
|
|
0174d18d8b | ||
|
|
6a9c262aa8 | ||
|
|
cfbcc5ece0 | ||
|
|
5e60c42a76 | ||
|
|
3dc28d7643 | ||
|
|
f2a0946a7a | ||
|
|
9a583c66f9 | ||
|
|
f66512db94 | ||
|
|
6fda58f137 | ||
|
|
a48cc9d201 | ||
|
|
cc2720c1d5 | ||
|
|
059aab4fa1 | ||
|
|
b0c7e49e58 | ||
|
|
a3611513d2 | ||
|
|
8190374c2b | ||
|
|
7551db445e | ||
|
|
0fb2269c4d | ||
|
|
f45376101c | ||
|
|
3259571e7d | ||
|
|
1df5e26001 | ||
|
|
7685123a7f | ||
|
|
a246b921a0 | ||
|
|
76d73f1835 | ||
|
|
eab39d8147 | ||
|
|
4c59aac0f9 | ||
|
|
043c33f1ce | ||
|
|
71be9b8c11 | ||
|
|
0ba7fd70c6 | ||
|
|
0481d42ad8 | ||
|
|
41b7e6b56e | ||
|
|
96c3d62496 | ||
|
|
fe9e31ef5e | ||
|
|
ce3c824712 | ||
|
|
e11da081f9 | ||
|
|
5f8f998d0e | ||
|
|
cadec0b126 | ||
|
|
d963775859 | ||
|
|
50a486656d | ||
|
|
eee788e26a | ||
|
|
d77b877cc9 | ||
|
|
ca8d951980 | ||
|
|
ae2a7222ce | ||
|
|
0b8ae8520f | ||
|
|
77cad88589 | ||
|
|
ab8d669887 | ||
|
|
f8b7ce9ef1 | ||
|
|
5df6f214e3 | ||
|
|
77f21c9c39 | ||
|
|
c9e6d865aa | ||
|
|
c65f41e816 | ||
|
|
bf731ede7e | ||
|
|
08333b8551 | ||
|
|
0209d7e6d6 | ||
|
|
fdd29a3d3f | ||
|
|
cf4a651bb8 | ||
|
|
0a27aca3f8 | ||
|
|
f25c4406e6 | ||
|
|
169004b1d5 | ||
|
|
576362abd7 | ||
|
|
ac49e4e4dc | ||
|
|
d39ceb58ac | ||
|
|
2ec2de1450 | ||
|
|
3e023c17cd | ||
|
|
f2e1efbeb7 | ||
|
|
e15afbce5d | ||
|
|
e53c9ccb24 | ||
|
|
af81fdb772 | ||
|
|
ea0be354a0 | ||
|
|
cd276aecd0 | ||
|
|
c892713104 | ||
|
|
ea24e026aa | ||
|
|
369e264e2e | ||
|
|
0d020a7892 | ||
|
|
5ee2136a71 | ||
|
|
47435a6162 | ||
|
|
8fa6ac68f0 | ||
|
|
74fb56fb5d | ||
|
|
4154a8395d | ||
|
|
c80b9fc8fc | ||
|
|
6235147ed5 | ||
|
|
d41b8c432d | ||
|
|
5f95589fae | ||
|
|
2309fd5c1d | ||
|
|
9629f4bcda | ||
|
|
de47492e91 | ||
|
|
2102ccd091 | ||
|
|
e3912d560b | ||
|
|
6e12e1e3d2 | ||
|
|
602f344a36 | ||
|
|
95ecccf6dc | ||
|
|
47dd07080f | ||
|
|
aa809cfeb3 | ||
|
|
31f2b8d8e1 | ||
|
|
774dfbdc34 | ||
|
|
a4cae68de0 | ||
|
|
238cdcdbe1 | ||
|
|
cf6c713a0b | ||
|
|
b95ac0a221 | ||
|
|
8923139414 | ||
|
|
5828e1995e | ||
|
|
8dca0247d2 | ||
|
|
9a950c5375 | ||
|
|
eb423903a4 | ||
|
|
ff0a94beda | ||
|
|
f80400843f | ||
|
|
6d8c07d375 | ||
|
|
d96fe5e079 | ||
|
|
ec1b2407a4 | ||
|
|
c07687699b | ||
|
|
41f14bcbc5 | ||
|
|
64eed38779 | ||
|
|
97934e2447 | ||
|
|
e88c4ca013 | ||
|
|
4562e83dc2 | ||
|
|
abdb109f3b | ||
|
|
875aec7044 | ||
|
|
71e856cf84 | ||
|
|
a90edffb7e | ||
|
|
423ecaf484 | ||
|
|
f08e66245a | ||
|
|
0d40cc5ea3 | ||
|
|
e8feb20e39 | ||
|
|
8200643017 | ||
|
|
a6f23c49b2 | ||
|
|
a5193774b0 | ||
|
|
da98d31ced | ||
|
|
3335713169 | ||
|
|
0beed01aa5 | ||
|
|
6753f35cac | ||
|
|
6583bb1a42 | ||
|
|
7561d0c338 | ||
|
|
7ccdb734c2 | ||
|
|
6ec0d2a946 | ||
|
|
8aa352b256 | ||
|
|
db86088426 | ||
|
|
9960c31685 | ||
|
|
6e36b51188 | ||
|
|
9dbd4aad77 | ||
|
|
e60853eabc | ||
|
|
696eb2b03a | ||
|
|
2b4fe33e00 | ||
|
|
2f5e8986cf | ||
|
|
bf2b4f114f | ||
|
|
4ed650a13d | ||
|
|
3ef1ce98b9 | ||
|
|
a7a03e9f96 | ||
|
|
5e122bd6d6 | ||
|
|
ab3c2583aa | ||
|
|
8f0dee773a | ||
|
|
4a7b85a967 | ||
|
|
b74657fb2e | ||
|
|
3661b98069 | ||
|
|
75f4af4d54 | ||
|
|
6c1d763119 | ||
|
|
8ce975ac82 | ||
|
|
7db53831a9 | ||
|
|
37f049490d | ||
|
|
7e7b6ccc7f | ||
|
|
4c7f565ff0 | ||
|
|
1f62b6b2e6 | ||
|
|
e224fdc80b | ||
|
|
71100500a8 | ||
|
|
230a685ea7 | ||
|
|
d48e427b1d | ||
|
|
99a01f4f8b | ||
|
|
4b025f10f7 | ||
|
|
92cbddf89c | ||
|
|
ca509a3362 | ||
|
|
367bf903b3 | ||
|
|
b0547ff0b4 | ||
|
|
fb4c7832f1 | ||
|
|
ba7f4b68c9 | ||
|
|
47ddd5a4cc | ||
|
|
b4106c44b0 | ||
|
|
357f455dec | ||
|
|
b9d80fa4e8 | ||
|
|
7badd3da4a | ||
|
|
80ce27d34e | ||
|
|
68eebcb0ff | ||
|
|
2dee2966df | ||
|
|
e0c0bb3480 | ||
|
|
9cf1cc2bd6 | ||
|
|
791896455a | ||
|
|
47399f92b0 | ||
|
|
d3cfb79ad6 | ||
|
|
ab714b8ac4 | ||
|
|
b105921c7d | ||
|
|
466c92e829 | ||
|
|
11714ff158 | ||
|
|
c0f7cc47f2 | ||
|
|
578beeb8a0 | ||
|
|
1a819f0053 | ||
|
|
d4f9c2efd4 | ||
|
|
846caff47f | ||
|
|
3f3ea2c539 | ||
|
|
d6dad5d05f | ||
|
|
46bb1e34b0 | ||
|
|
2b882e94af | ||
|
|
892b9238f4 | ||
|
|
e5687a18ca | ||
|
|
e56e68596b | ||
|
|
4077d9447b | ||
|
|
b5e30dac38 | ||
|
|
b275e598b5 | ||
|
|
64da45a9d3 | ||
|
|
038a060dfc | ||
|
|
2835089d6a | ||
|
|
e6c9351918 | ||
|
|
04507dc91f | ||
|
|
793c52616d | ||
|
|
d3d163972f | ||
|
|
202a710b26 | ||
|
|
14d27a46be | ||
|
|
cb1ce9969c | ||
|
|
ac761a3738 | ||
|
|
79fcf29af8 | ||
|
|
015f173ff8 | ||
|
|
a9947c3244 | ||
|
|
7eb01ff3e8 | ||
|
|
fb8c9106c7 | ||
|
|
bd91af200a | ||
|
|
6c702b81ac | ||
|
|
4c60f63c64 | ||
|
|
464ed10fa9 | ||
|
|
d478e58960 | ||
|
|
6938111357 | ||
|
|
2c08aac81a | ||
|
|
010ca3d10d | ||
|
|
a90e1e3f01 | ||
|
|
458f086669 | ||
|
|
3391459590 | ||
|
|
e4a7eed49d | ||
|
|
0837512964 | ||
|
|
aa2eb2d4a1 | ||
|
|
b7551e90e1 | ||
|
|
5bda52d4e8 | ||
|
|
66fa598af4 | ||
|
|
5ae220bef6 | ||
|
|
1556da0913 | ||
|
|
71a17e58f0 | ||
|
|
9eeabc077e | ||
|
|
363babe255 | ||
|
|
a141178255 | ||
|
|
829bd14145 | ||
|
|
a7a954c851 | ||
|
|
61b605b407 | ||
|
|
239421c5ef | ||
|
|
f5ca40e05f | ||
|
|
5cd2ef4c4a | ||
|
|
76ce918774 | ||
|
|
d2afe974f9 | ||
|
|
0fd0e12bfe | ||
|
|
63fadc9ffa | ||
|
|
110ad5835e | ||
|
|
912c9fdf0c | ||
|
|
f1694481a9 | ||
|
|
158763dea3 | ||
|
|
3770f3bbb6 | ||
|
|
a40f60a9b4 | ||
|
|
ed1c2bc655 | ||
|
|
b8c44f1aa4 | ||
|
|
997e103871 | ||
|
|
d73d1c8b73 | ||
|
|
1f9be97c22 | ||
|
|
f240117bef | ||
|
|
b37b0179c5 | ||
|
|
21735e06f7 | ||
|
|
397863bd66 | ||
|
|
6ecd72f845 | ||
|
|
1f7148a4f1 | ||
|
|
6ae395fa32 | ||
|
|
8076a00eed | ||
|
|
1c1702d818 | ||
|
|
775d3a373c | ||
|
|
c13245c7d8 | ||
|
|
688b265d5e | ||
|
|
1aa4e3d6ba | ||
|
|
3584abca16 | ||
|
|
afd5a62cd4 | ||
|
|
8550d443f5 | ||
|
|
96201e50ea | ||
|
|
945cfa3b7c | ||
|
|
8da07e8db0 | ||
|
|
bfd3fc02df | ||
|
|
024324273e | ||
|
|
96f5b4237d | ||
|
|
52fdbdfe66 | ||
|
|
ab8c2300b6 | ||
|
|
8304179aff | ||
|
|
5bd04a087c | ||
|
|
8f1fcc15af | ||
|
|
25bf2ce5cc | ||
|
|
922268fdd4 | ||
|
|
fa8927efe4 | ||
|
|
9b37359853 | ||
|
|
f7c507a5f8 | ||
|
|
7861578bd6 | ||
|
|
14e4043b67 | ||
|
|
469ba2cdfd | ||
|
|
4b9186b2eb | ||
|
|
d64d376c2a | ||
|
|
f717b82864 | ||
|
|
927ccdc43b | ||
|
|
fea94b2b36 | ||
|
|
5aa8d61f75 | ||
|
|
e7fb267df7 | ||
|
|
93c86ed5b9 | ||
|
|
5d791d2603 | ||
|
|
65726cd3a7 | ||
|
|
d26d9def80 | ||
|
|
12ec204ec7 | ||
|
|
a26408389a | ||
|
|
3fb600d50d | ||
|
|
67c547fdcd | ||
|
|
99e3f8128a | ||
|
|
95509f9914 | ||
|
|
74fb458bbc | ||
|
|
ae49ad8641 | ||
|
|
932fd4df61 | ||
|
|
badfcbaa1e | ||
|
|
35cafa6ca3 | ||
|
|
b0c9d8af32 | ||
|
|
b4e731cd93 | ||
|
|
db1321a6a2 | ||
|
|
6dfa5e3e58 | ||
|
|
55c05293d5 | ||
|
|
9f226bf8c3 | ||
|
|
f7b8f90740 | ||
|
|
c1e1b7104c | ||
|
|
0dd282672e | ||
|
|
48f66b6687 | ||
|
|
27933e2a8e | ||
|
|
df9f6ec829 | ||
|
|
8af4993b63 | ||
|
|
2b9d2495ef | ||
|
|
cc020a8c96 | ||
|
|
a288e74658 | ||
|
|
c4c3cf2d0e | ||
|
|
bef8e97d5d | ||
|
|
5a84460d6d | ||
|
|
acde0aae5a | ||
|
|
306ce4fde1 | ||
|
|
cced974bb2 | ||
|
|
56668c9fc5 | ||
|
|
76a7dc39e5 | ||
|
|
1df1d0eedb | ||
|
|
9ba1894b9b | ||
|
|
5ff0079ece | ||
|
|
cd82440ec7 | ||
|
|
a406b1dda8 | ||
|
|
0fde33e322 | ||
|
|
ac7d5e8d76 | ||
|
|
017f8cccec | ||
|
|
15ca5014f1 | ||
|
|
d623a8706f | ||
|
|
29377d55b6 | ||
|
|
eebaf97f5a | ||
|
|
d39dc8f3cc | ||
|
|
03fb75221c | ||
|
|
14efabbf1c | ||
|
|
7b83adbee6 | ||
|
|
77fb41c2f1 | ||
|
|
90fcfcd905 | ||
|
|
bd55604d1b | ||
|
|
41a5d99d55 | ||
|
|
80ab3edb94 | ||
|
|
32b71b2e5a | ||
|
|
3ec1182768 | ||
|
|
c5f00621c7 | ||
|
|
3ebcdd4133 | ||
|
|
1e595fe1e8 | ||
|
|
24a9693223 | ||
|
|
b918724280 | ||
|
|
923e8edafb | ||
|
|
020fd099f6 | ||
|
|
6a5292f6a8 | ||
|
|
56a2e9f5e7 | ||
|
|
0ae582e4f5 | ||
|
|
c2f9dc06cf | ||
|
|
b969f888ab | ||
|
|
acb297e9c2 | ||
|
|
155c1b222b | ||
|
|
717e4d5a7c | ||
|
|
7df93893dc | ||
|
|
5cdcd561e2 | ||
|
|
a53c336919 | ||
|
|
99131e7f8c | ||
|
|
c455676680 | ||
|
|
2a010f992a | ||
|
|
ca221bbc48 | ||
|
|
585d93dbba | ||
|
|
01d61fd9c6 | ||
|
|
f722c8f0bd | ||
|
|
1ceecdc871 | ||
|
|
17eb609916 | ||
|
|
f3a7a5bf76 | ||
|
|
d97b9ff755 | ||
|
|
0250dfcc19 | ||
|
|
52931fd548 | ||
|
|
c4855ca249 | ||
|
|
8e9c94dedb | ||
|
|
888429f409 | ||
|
|
7b23b19808 | ||
|
|
85b44d8a69 | ||
|
|
d92453f381 | ||
|
|
2cce031704 | ||
|
|
5e75642efd | ||
|
|
84326e4ab0 | ||
|
|
16ab951abf | ||
|
|
822f113ebb | ||
|
|
0ae2c2e4b2 | ||
|
|
39216e59d9 | ||
|
|
559e54ca60 | ||
|
|
8ef9a63b45 | ||
|
|
f27f773576 | ||
|
|
36e9c4bc50 | ||
|
|
4741fac42e | ||
|
|
1819965e0a | ||
|
|
7017001462 | ||
|
|
abcb012841 | ||
|
|
2df5bd30a6 | ||
|
|
9e356d6b25 | ||
|
|
fc6c75a2a2 | ||
|
|
49073da6d6 | ||
|
|
32462a072c | ||
|
|
f3d687e3fa | ||
|
|
bfc300c7ff | ||
|
|
7f9c98f21d | ||
|
|
ef314a5d6c | ||
|
|
a729cff987 | ||
|
|
422ec9fb62 | ||
|
|
8df238ec8a | ||
|
|
751506c484 | ||
|
|
65ef5afc27 | ||
|
|
2beef2f245 | ||
|
|
3033f24c26 | ||
|
|
64960da9e1 | ||
|
|
14dbd87bed | ||
|
|
ac76801159 | ||
|
|
aa6065aedd | ||
|
|
8b63ac78e0 | ||
|
|
f51be09e1f | ||
|
|
dc48196bd9 | ||
|
|
ea95b305ca | ||
|
|
f262d6120e | ||
|
|
bf46d0acff | ||
|
|
929a0fdccd | ||
|
|
17dbd05819 | ||
|
|
a6c1593645 | ||
|
|
03b4f50d39 | ||
|
|
1b989874a7 | ||
|
|
acd7b5af0f | ||
|
|
f274a96ce9 | ||
|
|
040b11bdf6 | ||
|
|
aee021bbb1 | ||
|
|
9e00a499a6 | ||
|
|
cefc9c0964 | ||
|
|
4c9af02326 | ||
|
|
dd55b8734a | ||
|
|
f0486968ba | ||
|
|
9287761d95 | ||
|
|
0e009366f8 | ||
|
|
b243a4bc30 | ||
|
|
6d4602b88a | ||
|
|
5fe1fe37a5 | ||
|
|
b83bd7c4ea | ||
|
|
b0114a327c | ||
|
|
feab45ef99 | ||
|
|
dbba67d1e7 | ||
|
|
e978fec61a | ||
|
|
d6fe588469 | ||
|
|
a21d647c11 | ||
|
|
7a01c3c3ec | ||
|
|
7fa67c9b9e | ||
|
|
ec1fb9f8dd | ||
|
|
7073bfb3ee | ||
|
|
0768b252fa | ||
|
|
6a48b8f003 | ||
|
|
1db07cdeef | ||
|
|
e28271a394 | ||
|
|
7fe357b8c0 | ||
|
|
af74c1453b | ||
|
|
6121413415 | ||
|
|
e25448235a | ||
|
|
b69a6c35b2 | ||
|
|
0ac29c5190 | ||
|
|
d484555024 | ||
|
|
9904e365a8 | ||
|
|
7df2049785 | ||
|
|
8e515dfeda | ||
|
|
db12250fd1 | ||
|
|
bf516a87e2 | ||
|
|
b44eda3f60 | ||
|
|
03e76e962e | ||
|
|
9fecdd713e | ||
|
|
43f010dd6d | ||
|
|
82d980209b | ||
|
|
5d4744a253 | ||
|
|
e38886a771 | ||
|
|
46305ca649 | ||
|
|
2fc8b658b7 | ||
|
|
1a338fb306 | ||
|
|
94055503e3 | ||
|
|
c64659e1b4 | ||
|
|
67ba7c7acc | ||
|
|
182497993b | ||
|
|
ede5e1584c | ||
|
|
553051f741 | ||
|
|
9064adc8a8 | ||
|
|
cecf509662 | ||
|
|
6584d398eb | ||
|
|
808094228c | ||
|
|
1d93ddec19 | ||
|
|
73805ff270 | ||
|
|
fbdcef2401 | ||
|
|
66de69c1fe | ||
|
|
48e4ffd15e | ||
|
|
29fd6f90c0 | ||
|
|
80ff38130e | ||
|
|
bf09cf1e36 | ||
|
|
e9570dd987 | ||
|
|
4a95384b34 | ||
|
|
cabf4bd2bc | ||
|
|
4ecba1ab97 | ||
|
|
b8d706c8c2 | ||
|
|
ae64a7117e | ||
|
|
92a5da9c8c | ||
|
|
9d4e2d1697 | ||
|
|
b1bdbbabfb | ||
|
|
7efb97483f | ||
|
|
ac4f5784a0 | ||
|
|
0873f85b54 | ||
|
|
5c176d2d9b | ||
|
|
44bd95612e | ||
|
|
c9b8ea0eef | ||
|
|
76394c09d4 | ||
|
|
0f773693bf | ||
|
|
241bb5d9d9 | ||
|
|
6cef0e4fa4 | ||
|
|
53c16ff047 | ||
|
|
21852a00a1 | ||
|
|
8697a3bcc8 | ||
|
|
4a67049113 | ||
|
|
b5e79422d5 | ||
|
|
0f595db60c | ||
|
|
8a218b4a96 | ||
|
|
ef98750027 | ||
|
|
602a00f93f | ||
|
|
2382050748 | ||
|
|
eddb7e70be | ||
|
|
73ba29158f | ||
|
|
c7129da5b6 | ||
|
|
94380d00d9 | ||
|
|
2a407092ab | ||
|
|
17e1986214 | ||
|
|
3ec404c47a | ||
|
|
b969f5dfac | ||
|
|
d7b8e71126 | ||
|
|
235f774e5f | ||
|
|
42a8a6280c | ||
|
|
b442bef320 | ||
|
|
b510fbfe3b | ||
|
|
9bc0f922aa | ||
|
|
3030f11525 | ||
|
|
dfcf4593fe | ||
|
|
55c75a25f0 | ||
|
|
2cb879f0c6 | ||
|
|
e15560107c | ||
|
|
c3a031686a | ||
|
|
39537d7cfe | ||
|
|
1dd419ced5 | ||
|
|
43fd3543df | ||
|
|
c7233dfcdc | ||
|
|
0ec4da960d | ||
|
|
35579a4902 | ||
|
|
97f6aff874 | ||
|
|
f9016d6662 | ||
|
|
e3d9771aa1 | ||
|
|
d97c143da6 | ||
|
|
309b790867 | ||
|
|
a987faedfa | ||
|
|
e7d3df2314 | ||
|
|
be6635e91d | ||
|
|
02971e7228 | ||
|
|
3b77e5a735 | ||
|
|
aa139c8f1a | ||
|
|
c24d8f144f | ||
|
|
1829c42c58 | ||
|
|
183168f332 | ||
|
|
860badcacc | ||
|
|
0254db9793 | ||
|
|
1aadf856c9 | ||
|
|
1579de3cae | ||
|
|
4b9fa5d0e2 | ||
|
|
4c82284d2e | ||
|
|
560394798f | ||
|
|
19f0ba0eb9 | ||
|
|
774d4cb758 | ||
|
|
d7eabb8031 | ||
|
|
fe42739cc8 | ||
|
|
b9df35f714 | ||
|
|
6ce296da12 | ||
|
|
2c906c407c | ||
|
|
be0fd1d52d | ||
|
|
e96170fe5c | ||
|
|
0f017b56f3 | ||
|
|
c86b40cca0 | ||
|
|
66935fb9ee | ||
|
|
b7de794622 | ||
|
|
77724f70e9 | ||
|
|
416e1cea9b | ||
|
|
72831f6b28 | ||
|
|
a6597483af | ||
|
|
98c8138663 | ||
|
|
c6b24543fc | ||
|
|
022d2f886c | ||
|
|
2db15a9583 | ||
|
|
1d575ccd4e | ||
|
|
cafa1d882f | ||
|
|
7afdaf8496 | ||
|
|
4d6d7285b0 | ||
|
|
ec0d1be577 | ||
|
|
9814ddb601 | ||
|
|
4509e32e63 | ||
|
|
f2ebc4a836 | ||
|
|
783a8cda24 | ||
|
|
9a062b8ea6 | ||
|
|
0f027a72bf | ||
|
|
0d5b334ee8 | ||
|
|
f480d1a7ef | ||
|
|
d4615d0889 | ||
|
|
cb4a18a7ba | ||
|
|
f079e487ae | ||
|
|
da0912126b | ||
|
|
b8c2013512 | ||
|
|
9b228b5416 | ||
|
|
daeb276a2b | ||
|
|
cc08742454 | ||
|
|
4a82aab569 | ||
|
|
7a191398ca | ||
|
|
775dfad297 | ||
|
|
47d26be760 | ||
|
|
f0e0677b87 | ||
|
|
c5f7747fc5 | ||
|
|
0de5f33e31 | ||
|
|
7d850f7b9a | ||
|
|
5487529368 | ||
|
|
4fbe9cf202 | ||
|
|
3fd59039bd | ||
|
|
b9e356b998 | ||
|
|
a7e7caa486 | ||
|
|
2cb39180cc | ||
|
|
279e66138d | ||
|
|
b6c0428e8c | ||
|
|
d1c4ffae89 | ||
|
|
07c39559ea | ||
|
|
7b16197361 | ||
|
|
d7c4b02a57 | ||
|
|
9d299469d2 | ||
|
|
d581ba40ba | ||
|
|
f8740f0d6c | ||
|
|
ce73abe054 | ||
|
|
ab66becaae | ||
|
|
205c7f26af | ||
|
|
cbcdd5ffaf | ||
|
|
bf586e8844 | ||
|
|
6dc79dc226 | ||
|
|
11fce25a15 | ||
|
|
c4e63f99f9 | ||
|
|
4f43d38ca8 | ||
|
|
3f6615ac26 | ||
|
|
e7c5954c10 | ||
|
|
f79c163bbf | ||
|
|
0f54f1ec5f | ||
|
|
cbcbedd0de | ||
|
|
ac79ed19ef | ||
|
|
e8694d4dc3 | ||
|
|
9e92b6eac6 | ||
|
|
abbae27982 | ||
|
|
a28c4b363d | ||
|
|
012d2c60fa | ||
|
|
67720c8bde | ||
|
|
9241ecf45d | ||
|
|
9422211d5f | ||
|
|
df40057b21 | ||
|
|
ea08466d34 | ||
|
|
b0b973c39b | ||
|
|
c6d3292738 | ||
|
|
67f601cd46 | ||
|
|
b9489861a3 | ||
|
|
856f8ec1fd | ||
|
|
c537514d46 | ||
|
|
dc0ce039f3 | ||
|
|
f00b639b96 | ||
|
|
29ed796c17 | ||
|
|
9f0dd6e539 | ||
|
|
5e56bbe09a | ||
|
|
b902c3ea50 | ||
|
|
ef3c39bbd2 | ||
|
|
eec423abe9 | ||
|
|
ae1c046e12 | ||
|
|
4b997ae46d | ||
|
|
72208becb0 | ||
|
|
9592053859 | ||
|
|
4c1b300ada | ||
|
|
fbcc200418 | ||
|
|
80d950d94e | ||
|
|
04c20e75ea | ||
|
|
a437694a17 | ||
|
|
ca7f60db5f | ||
|
|
72d573f693 | ||
|
|
4e2589ff81 | ||
|
|
4ba8e07425 | ||
|
|
e752f0a673 | ||
|
|
f8abb112f2 | ||
|
|
ae2188a435 | ||
|
|
1f4b8642e8 | ||
|
|
14108d7878 | ||
|
|
d8340da756 | ||
|
|
a66e66c79d | ||
|
|
7ce8788b06 | ||
|
|
012e623ddd | ||
|
|
87c3d53180 | ||
|
|
6f4fcb983e | ||
|
|
24284459c7 | ||
|
|
bdf7b40c5c | ||
|
|
e66a9225f3 | ||
|
|
c279fec192 | ||
|
|
31a9cf6417 | ||
|
|
9275d91c79 | ||
|
|
26106d662e | ||
|
|
1c4e3efea0 | ||
|
|
a3946b8956 | ||
|
|
8ed9c00d5e | ||
|
|
bad775715a | ||
|
|
3cca0dc7f0 | ||
|
|
37e395fd1c | ||
|
|
9475bef4d7 | ||
|
|
441f273f19 | ||
|
|
4a279a680e | ||
|
|
66ad372500 | ||
|
|
432e5b550e | ||
|
|
57606047ec | ||
|
|
1b66bbe998 | ||
|
|
c6d0f9e758 | ||
|
|
f399d30764 | ||
|
|
9de9074c92 | ||
|
|
8e517eca68 | ||
|
|
e18571393d | ||
|
|
e0c809ad23 | ||
|
|
a96ccf8fde | ||
|
|
a0df55104e | ||
|
|
413dfc0c4b | ||
|
|
0f23566558 | ||
|
|
d69e36ec59 | ||
|
|
fdaac8e0ca | ||
|
|
2f0e2ba826 | ||
|
|
a3e79a46f6 | ||
|
|
e4f4dddba3 | ||
|
|
cbc3fbb4d7 | ||
|
|
95a9bd85c4 | ||
|
|
54f2c14cce | ||
|
|
aa42f4231f | ||
|
|
11a25f75bd | ||
|
|
5cccdadf2e | ||
|
|
95ca44a718 | ||
|
|
4171b6724e | ||
|
|
c8581b06e1 | ||
|
|
5798eee6be | ||
|
|
4167a3f5f7 | ||
|
|
d18554c30d | ||
|
|
7489b0e72b | ||
|
|
77bdddf016 | ||
|
|
6638710b9e | ||
|
|
abf04205b3 | ||
|
|
572022a350 | ||
|
|
a28e21b141 | ||
|
|
18d5a1efa8 | ||
|
|
829a8c19a0 | ||
|
|
c94ed49efd | ||
|
|
653ace55c3 | ||
|
|
919220c7e6 | ||
|
|
53b096c0d7 | ||
|
|
e912bd55be | ||
|
|
541d783983 | ||
|
|
f8cd0672bb | ||
|
|
22881c999e | ||
|
|
613d281e87 | ||
|
|
1304eb3418 | ||
|
|
34c20c06c8 | ||
|
|
7083006b61 | ||
|
|
0db9031c79 | ||
|
|
b5bc15305b | ||
|
|
5b90d8fe42 | ||
|
|
f7ada560ce | ||
|
|
5beb6bf070 | ||
|
|
eba6ce06c3 | ||
|
|
79abfbd9df | ||
|
|
fe395f0e4d | ||
|
|
32de385eca | ||
|
|
72395ba977 | ||
|
|
65e5eb8a62 | ||
|
|
e4c829efe9 | ||
|
|
c2b5a0396a | ||
|
|
b7a33d7e91 | ||
|
|
97c76f1f30 | ||
|
|
0ea8c6c219 | ||
|
|
d7ff2f976c | ||
|
|
f85ec712b0 | ||
|
|
54edbf65ff | ||
|
|
139142e440 | ||
|
|
5f36b68d22 | ||
|
|
e0e9960dd1 | ||
|
|
64ac51446d | ||
|
|
c24f8954be | ||
|
|
d1c359ef29 | ||
|
|
a4c3a31b8f | ||
|
|
f358eeb891 | ||
|
|
80247291c6 | ||
|
|
79b5bdbfde | ||
|
|
6c6736816c | ||
|
|
0ce27e715e | ||
|
|
aca1b98f52 | ||
|
|
ca00502788 | ||
|
|
c8a87bb62d | ||
|
|
32f67e309f | ||
|
|
fabc65da32 | ||
|
|
7534d71640 | ||
|
|
5f81391263 | ||
|
|
e321abe43d | ||
|
|
f82cb06afb | ||
|
|
f545feee64 | ||
|
|
2d9b0a4472 | ||
|
|
1bd4c2ad23 | ||
|
|
e295b8f110 | ||
|
|
1be4e760e2 | ||
|
|
d5e498d47f | ||
|
|
47a2d8e1d9 | ||
|
|
041956f6a3 | ||
|
|
767eb40238 | ||
|
|
35ad48b848 | ||
|
|
45d9635fd3 | ||
|
|
dc37df8c7a | ||
|
|
9f7d9e6dda | ||
|
|
fdd6528ba2 | ||
|
|
ea3bba5a66 | ||
|
|
f060dfc422 | ||
|
|
ab70794ddb | ||
|
|
d4471637ef | ||
|
|
2aef54d429 | ||
|
|
aed0f5a231 | ||
|
|
286306853e | ||
|
|
248f3aed22 | ||
|
|
49e15044ef | ||
|
|
38128cb9df | ||
|
|
c792d4129a | ||
|
|
0cc811d7d6 | ||
|
|
d7167ff7ce | ||
|
|
b901416b90 | ||
|
|
cddd334050 | ||
|
|
0716a98eb3 | ||
|
|
f9ced95a9b | ||
|
|
924fcfd900 | ||
|
|
c0a462cac2 | ||
|
|
6986bb5e12 | ||
|
|
f89e1690df | ||
|
|
2172cb626a | ||
|
|
e3b343ece7 | ||
|
|
0e23c487da | ||
|
|
6208338a6c | ||
|
|
d51f3e4069 | ||
|
|
690b491af1 | ||
|
|
96d43a873a | ||
|
|
3101f53720 | ||
|
|
a6bb9b17d8 | ||
|
|
d84a8ffdf7 | ||
|
|
be70b86c57 | ||
|
|
e0a9932161 | ||
|
|
e2502a97c1 | ||
|
|
24e1072aac | ||
|
|
c0e84df8e8 | ||
|
|
8dd28bb560 | ||
|
|
f0103595dd | ||
|
|
d3242aee16 | ||
|
|
a9decb5584 | ||
|
|
3fea6a28da | ||
|
|
80b8099fd8 | ||
|
|
e564062522 | ||
|
|
854509fec0 | ||
|
|
344219645b | ||
|
|
865069c12e | ||
|
|
a59562283f | ||
|
|
187d379db6 | ||
|
|
a48a2d7635 | ||
|
|
a6140194ff | ||
|
|
e80eab1fbc | ||
|
|
e9bfb1166d | ||
|
|
5927e15bc7 | ||
|
|
30f3b75b33 | ||
|
|
77d4c7e337 | ||
|
|
f75dfbf23d | ||
|
|
637b388809 | ||
|
|
8323a9038d | ||
|
|
e6c4b52f28 | ||
|
|
49911d4df2 | ||
|
|
f4059d0c7d | ||
|
|
e632b0929b | ||
|
|
f5c04d64b7 | ||
|
|
fc98edd936 | ||
|
|
73d361dd5f | ||
|
|
0b21c30b1a | ||
|
|
953acd56a4 | ||
|
|
f4ae143720 | ||
|
|
569771549a | ||
|
|
2523aa73cb | ||
|
|
ee52dc4e54 | ||
|
|
3fca851a20 | ||
|
|
e2a83d7109 | ||
|
|
de2d03e12f | ||
|
|
3fc4c539aa | ||
|
|
b4dc4069a2 | ||
|
|
137e609016 | ||
|
|
b61c9ceca8 | ||
|
|
e2b8cec05b | ||
|
|
98c54107df | ||
|
|
dab702b357 | ||
|
|
b564f7c7d5 | ||
|
|
b7e5a5c451 | ||
|
|
63c2fc02ce | ||
|
|
bb07022b66 | ||
|
|
e300c9d819 | ||
|
|
0e519eea8e | ||
|
|
3ef0f08af5 | ||
|
|
4c6dde37b9 | ||
|
|
f7b4c48bba | ||
|
|
b47fb00ac0 | ||
|
|
b5a36cc9ad | ||
|
|
0524d9e5e8 | ||
|
|
d3485d9607 | ||
|
|
29a9fe222a | ||
|
|
e09e9ff6e5 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -19,8 +19,10 @@
|
|||||||
/stamp-h1
|
/stamp-h1
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
|
examples/anim_diff
|
||||||
examples/[cdv]webp
|
examples/[cdv]webp
|
||||||
examples/gif2webp
|
examples/gif2webp
|
||||||
|
examples/img2webp
|
||||||
examples/webpmux
|
examples/webpmux
|
||||||
src/webp/config.h*
|
src/webp/config.h*
|
||||||
src/webp/stamp-h1
|
src/webp/stamp-h1
|
||||||
@@ -30,3 +32,10 @@ src/webp/stamp-h1
|
|||||||
*.pdb
|
*.pdb
|
||||||
/iosbuild
|
/iosbuild
|
||||||
/WebP.framework
|
/WebP.framework
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
.gradle
|
||||||
|
/build
|
||||||
|
extras/get_disto
|
||||||
|
extras/webp_quality
|
||||||
|
|||||||
4
.mailmap
4
.mailmap
@@ -6,3 +6,7 @@ Vikas Arora <vikasa@google.com>
|
|||||||
<vikasa@google.com> <vikasa@gmail.com>
|
<vikasa@google.com> <vikasa@gmail.com>
|
||||||
<vikasa@google.com> <vikaas.arora@gmail.com>
|
<vikasa@google.com> <vikaas.arora@gmail.com>
|
||||||
<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
|
<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
|
||||||
|
<vrabaud@google.com> <vincent.rabaud@gmail.com>
|
||||||
|
Tamar Levy <tamar.levy@intel.com>
|
||||||
|
<qrczak@google.com> <qrczak>
|
||||||
|
Hui Su <huisu@google.com>
|
||||||
|
|||||||
16
AUTHORS
16
AUTHORS
@@ -2,23 +2,37 @@ Contributors:
|
|||||||
- Charles Munger (clm at google dot com)
|
- Charles Munger (clm at google dot com)
|
||||||
- Christian Duvivier (cduvivier at google dot com)
|
- Christian Duvivier (cduvivier at google dot com)
|
||||||
- Djordje Pesut (djordje dot pesut at imgtec dot com)
|
- Djordje Pesut (djordje dot pesut at imgtec dot com)
|
||||||
|
- Hui Su (huisu at google dot com)
|
||||||
- James Zern (jzern at google dot com)
|
- James Zern (jzern at google dot com)
|
||||||
- Jan Engelhardt (jengelh at medozas dot de)
|
- Jan Engelhardt (jengelh at medozas dot de)
|
||||||
|
- Jehan (jehan at girinstud dot io)
|
||||||
- Johann (johann dot koenig at duck dot com)
|
- Johann (johann dot koenig at duck dot com)
|
||||||
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
|
- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
|
||||||
- Jyrki Alakuijala (jyrki at google dot com)
|
- Jyrki Alakuijala (jyrki at google dot com)
|
||||||
- levytamar82 (tamar dot levy at intel dot com)
|
- Lode Vandevenne (lode at google dot com)
|
||||||
- Lou Quillio (louquillio at google dot com)
|
- Lou Quillio (louquillio at google dot com)
|
||||||
- Mans Rullgard (mans at mansr dot com)
|
- Mans Rullgard (mans at mansr dot com)
|
||||||
|
- Marcin Kowalczyk (qrczak at google dot com)
|
||||||
- Martin Olsson (mnemo at minimum dot se)
|
- Martin Olsson (mnemo at minimum dot se)
|
||||||
- Mikołaj Zalewski (mikolajz at google dot com)
|
- Mikołaj Zalewski (mikolajz at google dot com)
|
||||||
|
- Mislav Bradac (mislavm at google dot com)
|
||||||
|
- Nico Weber (thakis at chromium dot org)
|
||||||
- Noel Chromium (noel at chromium dot org)
|
- Noel Chromium (noel at chromium dot org)
|
||||||
|
- Owen Rodley (orodley at google dot com)
|
||||||
|
- Parag Salasakar (img dot mips1 at gmail dot com)
|
||||||
- Pascal Massimino (pascal dot massimino at gmail dot com)
|
- Pascal Massimino (pascal dot massimino at gmail dot com)
|
||||||
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
|
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
|
||||||
- Pierre Joye (pierre dot php at gmail dot com)
|
- Pierre Joye (pierre dot php at gmail dot com)
|
||||||
|
- Sam Clegg (sbc at chromium dot org)
|
||||||
|
- Scott Hancher (seh at google dot com)
|
||||||
- Scott LaVarnway (slavarnway at google dot com)
|
- Scott LaVarnway (slavarnway at google dot com)
|
||||||
- Scott Talbot (s at chikachow dot org)
|
- Scott Talbot (s at chikachow dot org)
|
||||||
- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
|
- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
|
||||||
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
|
- Somnath Banerjee (somnath dot banerjee at gmail dot com)
|
||||||
|
- Sriraman Tallam (tmsriram at google dot com)
|
||||||
|
- Tamar Levy (tamar dot levy at intel dot com)
|
||||||
|
- Timothy Gu (timothygu99 at gmail dot com)
|
||||||
- Urvang Joshi (urvang at google dot com)
|
- Urvang Joshi (urvang at google dot com)
|
||||||
- Vikas Arora (vikasa at google dot com)
|
- Vikas Arora (vikasa at google dot com)
|
||||||
|
- Vincent Rabaud (vrabaud at google dot com)
|
||||||
|
- Yang Zhang (yang dot zhang at arm dot com)
|
||||||
|
|||||||
264
Android.mk
264
Android.mk
@@ -1,6 +1,7 @@
|
|||||||
LOCAL_PATH := $(call my-dir)
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
|
WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
|
||||||
|
WEBP_CFLAGS += -fvisibility=hidden
|
||||||
|
|
||||||
ifeq ($(APP_OPTIM),release)
|
ifeq ($(APP_OPTIM),release)
|
||||||
WEBP_CFLAGS += -finline-functions -ffast-math \
|
WEBP_CFLAGS += -finline-functions -ffast-math \
|
||||||
@@ -10,90 +11,195 @@ ifeq ($(APP_OPTIM),release)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
||||||
# Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
|
# Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
|
||||||
# instructions to be generated for armv7a code. Instead target the neon code
|
# instructions to be generated for armv7a code. Instead target the neon code
|
||||||
# specifically.
|
# specifically.
|
||||||
NEON := c.neon
|
NEON := c.neon
|
||||||
|
USE_CPUFEATURES := yes
|
||||||
else
|
else
|
||||||
NEON := c
|
NEON := c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
dec_srcs := \
|
||||||
src/dec/alpha.c \
|
src/dec/alpha_dec.c \
|
||||||
src/dec/buffer.c \
|
src/dec/buffer_dec.c \
|
||||||
src/dec/frame.c \
|
src/dec/frame_dec.c \
|
||||||
src/dec/idec.c \
|
src/dec/idec_dec.c \
|
||||||
src/dec/io.c \
|
src/dec/io_dec.c \
|
||||||
src/dec/quant.c \
|
src/dec/quant_dec.c \
|
||||||
src/dec/tree.c \
|
src/dec/tree_dec.c \
|
||||||
src/dec/vp8.c \
|
src/dec/vp8_dec.c \
|
||||||
src/dec/vp8l.c \
|
src/dec/vp8l_dec.c \
|
||||||
src/dec/webp.c \
|
src/dec/webp_dec.c \
|
||||||
|
|
||||||
|
demux_srcs := \
|
||||||
|
src/demux/anim_decode.c \
|
||||||
|
src/demux/demux.c \
|
||||||
|
|
||||||
|
dsp_dec_srcs := \
|
||||||
src/dsp/alpha_processing.c \
|
src/dsp/alpha_processing.c \
|
||||||
|
src/dsp/alpha_processing_mips_dsp_r2.c \
|
||||||
|
src/dsp/alpha_processing_neon.$(NEON) \
|
||||||
|
src/dsp/alpha_processing_sse2.c \
|
||||||
|
src/dsp/alpha_processing_sse41.c \
|
||||||
|
src/dsp/argb.c \
|
||||||
|
src/dsp/argb_mips_dsp_r2.c \
|
||||||
|
src/dsp/argb_sse2.c \
|
||||||
src/dsp/cpu.c \
|
src/dsp/cpu.c \
|
||||||
src/dsp/dec.c \
|
src/dsp/dec.c \
|
||||||
src/dsp/dec_clip_tables.c \
|
src/dsp/dec_clip_tables.c \
|
||||||
src/dsp/dec_mips32.c \
|
src/dsp/dec_mips32.c \
|
||||||
|
src/dsp/dec_mips_dsp_r2.c \
|
||||||
|
src/dsp/dec_msa.c \
|
||||||
src/dsp/dec_neon.$(NEON) \
|
src/dsp/dec_neon.$(NEON) \
|
||||||
src/dsp/dec_sse2.c \
|
src/dsp/dec_sse2.c \
|
||||||
src/dsp/enc.c \
|
src/dsp/dec_sse41.c \
|
||||||
src/dsp/enc_avx2.c \
|
src/dsp/filters.c \
|
||||||
src/dsp/enc_mips32.c \
|
src/dsp/filters_mips_dsp_r2.c \
|
||||||
src/dsp/enc_neon.$(NEON) \
|
src/dsp/filters_msa.c \
|
||||||
src/dsp/enc_sse2.c \
|
src/dsp/filters_neon.$(NEON) \
|
||||||
|
src/dsp/filters_sse2.c \
|
||||||
src/dsp/lossless.c \
|
src/dsp/lossless.c \
|
||||||
src/dsp/lossless_mips32.c \
|
src/dsp/lossless_mips_dsp_r2.c \
|
||||||
|
src/dsp/lossless_msa.c \
|
||||||
src/dsp/lossless_neon.$(NEON) \
|
src/dsp/lossless_neon.$(NEON) \
|
||||||
src/dsp/lossless_sse2.c \
|
src/dsp/lossless_sse2.c \
|
||||||
|
src/dsp/rescaler.c \
|
||||||
|
src/dsp/rescaler_mips32.c \
|
||||||
|
src/dsp/rescaler_mips_dsp_r2.c \
|
||||||
|
src/dsp/rescaler_msa.c \
|
||||||
|
src/dsp/rescaler_neon.$(NEON) \
|
||||||
|
src/dsp/rescaler_sse2.c \
|
||||||
src/dsp/upsampling.c \
|
src/dsp/upsampling.c \
|
||||||
|
src/dsp/upsampling_mips_dsp_r2.c \
|
||||||
|
src/dsp/upsampling_msa.c \
|
||||||
src/dsp/upsampling_neon.$(NEON) \
|
src/dsp/upsampling_neon.$(NEON) \
|
||||||
src/dsp/upsampling_sse2.c \
|
src/dsp/upsampling_sse2.c \
|
||||||
src/dsp/yuv.c \
|
src/dsp/yuv.c \
|
||||||
src/dsp/yuv_mips32.c \
|
src/dsp/yuv_mips32.c \
|
||||||
|
src/dsp/yuv_mips_dsp_r2.c \
|
||||||
src/dsp/yuv_sse2.c \
|
src/dsp/yuv_sse2.c \
|
||||||
src/enc/alpha.c \
|
|
||||||
src/enc/analysis.c \
|
dsp_enc_srcs := \
|
||||||
src/enc/backward_references.c \
|
src/dsp/cost.c \
|
||||||
src/enc/config.c \
|
src/dsp/cost_mips32.c \
|
||||||
src/enc/cost.c \
|
src/dsp/cost_mips_dsp_r2.c \
|
||||||
src/enc/filter.c \
|
src/dsp/cost_sse2.c \
|
||||||
src/enc/frame.c \
|
src/dsp/enc.c \
|
||||||
src/enc/histogram.c \
|
src/dsp/enc_avx2.c \
|
||||||
src/enc/iterator.c \
|
src/dsp/enc_mips32.c \
|
||||||
src/enc/picture.c \
|
src/dsp/enc_mips_dsp_r2.c \
|
||||||
src/enc/picture_csp.c \
|
src/dsp/enc_msa.c \
|
||||||
src/enc/picture_psnr.c \
|
src/dsp/enc_neon.$(NEON) \
|
||||||
src/enc/picture_rescale.c \
|
src/dsp/enc_sse2.c \
|
||||||
src/enc/picture_tools.c \
|
src/dsp/enc_sse41.c \
|
||||||
src/enc/quant.c \
|
src/dsp/lossless_enc.c \
|
||||||
src/enc/syntax.c \
|
src/dsp/lossless_enc_mips32.c \
|
||||||
src/enc/token.c \
|
src/dsp/lossless_enc_mips_dsp_r2.c \
|
||||||
src/enc/tree.c \
|
src/dsp/lossless_enc_msa.c \
|
||||||
src/enc/vp8l.c \
|
src/dsp/lossless_enc_neon.$(NEON) \
|
||||||
src/enc/webpenc.c \
|
src/dsp/lossless_enc_sse2.c \
|
||||||
src/utils/bit_reader.c \
|
src/dsp/lossless_enc_sse41.c \
|
||||||
src/utils/bit_writer.c \
|
|
||||||
src/utils/color_cache.c \
|
enc_srcs := \
|
||||||
src/utils/filters.c \
|
src/enc/alpha_enc.c \
|
||||||
src/utils/huffman.c \
|
src/enc/analysis_enc.c \
|
||||||
src/utils/huffman_encode.c \
|
src/enc/backward_references_enc.c \
|
||||||
src/utils/quant_levels.c \
|
src/enc/config_enc.c \
|
||||||
src/utils/quant_levels_dec.c \
|
src/enc/cost_enc.c \
|
||||||
src/utils/random.c \
|
src/enc/delta_palettization_enc.c \
|
||||||
src/utils/rescaler.c \
|
src/enc/filter_enc.c \
|
||||||
src/utils/thread.c \
|
src/enc/frame_enc.c \
|
||||||
|
src/enc/histogram_enc.c \
|
||||||
|
src/enc/iterator_enc.c \
|
||||||
|
src/enc/near_lossless_enc.c \
|
||||||
|
src/enc/picture_enc.c \
|
||||||
|
src/enc/picture_csp_enc.c \
|
||||||
|
src/enc/picture_psnr_enc.c \
|
||||||
|
src/enc/picture_rescale_enc.c \
|
||||||
|
src/enc/picture_tools_enc.c \
|
||||||
|
src/enc/predictor_enc.c \
|
||||||
|
src/enc/quant_enc.c \
|
||||||
|
src/enc/syntax_enc.c \
|
||||||
|
src/enc/token_enc.c \
|
||||||
|
src/enc/tree_enc.c \
|
||||||
|
src/enc/vp8l_enc.c \
|
||||||
|
src/enc/webp_enc.c \
|
||||||
|
|
||||||
|
mux_srcs := \
|
||||||
|
src/mux/anim_encode.c \
|
||||||
|
src/mux/muxedit.c \
|
||||||
|
src/mux/muxinternal.c \
|
||||||
|
src/mux/muxread.c \
|
||||||
|
|
||||||
|
utils_dec_srcs := \
|
||||||
|
src/utils/bit_reader_utils.c \
|
||||||
|
src/utils/color_cache_utils.c \
|
||||||
|
src/utils/filters_utils.c \
|
||||||
|
src/utils/huffman_utils.c \
|
||||||
|
src/utils/quant_levels_dec_utils.c \
|
||||||
|
src/utils/random_utils.c \
|
||||||
|
src/utils/rescaler_utils.c \
|
||||||
|
src/utils/thread_utils.c \
|
||||||
src/utils/utils.c \
|
src/utils/utils.c \
|
||||||
|
|
||||||
|
utils_enc_srcs := \
|
||||||
|
src/utils/bit_writer_utils.c \
|
||||||
|
src/utils/huffman_encode_utils.c \
|
||||||
|
src/utils/quant_levels_utils.c \
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libwebpdecoder
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
$(dec_srcs) \
|
||||||
|
$(dsp_dec_srcs) \
|
||||||
|
$(utils_dec_srcs) \
|
||||||
|
|
||||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
|
||||||
|
|
||||||
# prefer arm over thumb mode for performance gains
|
# prefer arm over thumb mode for performance gains
|
||||||
LOCAL_ARM_MODE := arm
|
LOCAL_ARM_MODE := arm
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := cpufeatures
|
ifeq ($(USE_CPUFEATURES),yes)
|
||||||
|
LOCAL_STATIC_LIBRARIES := cpufeatures
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_MODULE := webpdecoder_static
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_SHARED),1)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
|
||||||
|
|
||||||
|
LOCAL_MODULE := webpdecoder
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
endif # ENABLE_SHARED=1
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libwebp
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
$(dsp_enc_srcs) \
|
||||||
|
$(enc_srcs) \
|
||||||
|
$(utils_enc_srcs) \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
|
||||||
|
|
||||||
|
# prefer arm over thumb mode for performance gains
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
|
||||||
|
LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
|
||||||
|
|
||||||
LOCAL_MODULE := webp
|
LOCAL_MODULE := webp
|
||||||
|
|
||||||
@@ -103,6 +209,58 @@ else
|
|||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(LOCAL_PATH)/examples/Android.mk
|
################################################################################
|
||||||
|
# libwebpdemux
|
||||||
|
|
||||||
$(call import-module,android/cpufeatures)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(demux_srcs)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
|
||||||
|
|
||||||
|
# prefer arm over thumb mode for performance gains
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
|
||||||
|
LOCAL_MODULE := webpdemux
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_SHARED),1)
|
||||||
|
LOCAL_SHARED_LIBRARIES := webp
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
else
|
||||||
|
LOCAL_STATIC_LIBRARIES := webp
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
endif
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libwebpmux
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(mux_srcs)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
|
||||||
|
|
||||||
|
# prefer arm over thumb mode for performance gains
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
|
||||||
|
LOCAL_MODULE := webpmux
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_SHARED),1)
|
||||||
|
LOCAL_SHARED_LIBRARIES := webp
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
else
|
||||||
|
LOCAL_STATIC_LIBRARIES := webp
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
endif
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
WEBP_SRC_PATH := $(LOCAL_PATH)
|
||||||
|
include $(WEBP_SRC_PATH)/imageio/Android.mk
|
||||||
|
include $(WEBP_SRC_PATH)/examples/Android.mk
|
||||||
|
|
||||||
|
ifeq ($(USE_CPUFEATURES),yes)
|
||||||
|
$(call import-module,android/cpufeatures)
|
||||||
|
endif
|
||||||
|
|||||||
189
CMakeLists.txt
Normal file
189
CMakeLists.txt
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8.7)
|
||||||
|
|
||||||
|
project(libwebp C)
|
||||||
|
|
||||||
|
# Options for coder / decoder executables.
|
||||||
|
option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." OFF)
|
||||||
|
option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." OFF)
|
||||||
|
option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." OFF)
|
||||||
|
option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." OFF)
|
||||||
|
option(WEBP_EXPERIMENTAL_FEATURES "Build with experimental features." OFF)
|
||||||
|
option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces." OFF)
|
||||||
|
|
||||||
|
set(WEBP_DEP_LIBRARIES)
|
||||||
|
set(WEBP_DEP_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Release" CACHE
|
||||||
|
"Build type: Release, Debug or RelWithDebInfo" STRING FORCE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(cmake/config.h.cmake)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Options.
|
||||||
|
if(WEBP_ENABLE_SWAP_16BIT_CSP)
|
||||||
|
add_definitions(-DWEBP_SWAP_16BIT_CSP)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Android only.
|
||||||
|
if(ANDROID)
|
||||||
|
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
|
||||||
|
add_library(cpufeatures STATIC
|
||||||
|
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
|
||||||
|
)
|
||||||
|
target_link_libraries(cpufeatures dl)
|
||||||
|
set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures)
|
||||||
|
set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS}
|
||||||
|
${ANDROID_NDK}/sources/android/cpufeatures
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# WebP source files.
|
||||||
|
# Read the Makefile.am to get the source files.
|
||||||
|
|
||||||
|
function(parse_Makefile_am FOLDER VAR)
|
||||||
|
file(READ ${FOLDER}/Makefile.am MAKEFILE_AM)
|
||||||
|
string(REGEX MATCHALL "_SOURCES \\+= [^\n]*"
|
||||||
|
FILES_PER_LINE ${MAKEFILE_AM}
|
||||||
|
)
|
||||||
|
set(SRCS ${${VAR}})
|
||||||
|
foreach(FILES ${FILES_PER_LINE})
|
||||||
|
string(SUBSTRING ${FILES} 12 -1 FILES)
|
||||||
|
string(REGEX MATCHALL "[0-9a-z\\._]+"
|
||||||
|
FILES ${FILES}
|
||||||
|
)
|
||||||
|
foreach(FILE ${FILES})
|
||||||
|
list(APPEND SRCS ${FOLDER}/${FILE})
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
set(${VAR} ${SRCS} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(WEBP_SRCS)
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/dec "WEBP_SRCS")
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/demux "WEBP_SRCS")
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/dsp "WEBP_SRCS")
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/enc "WEBP_SRCS")
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/utils "WEBP_SRCS")
|
||||||
|
|
||||||
|
# Remove the files specific to SIMD we don't use.
|
||||||
|
foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
|
||||||
|
list(REMOVE_ITEM WEBP_SRCS ${FILE})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Build the library.
|
||||||
|
add_definitions(-Wall)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/ ${WEBP_DEP_INCLUDE_DIRS})
|
||||||
|
add_library(webp ${WEBP_SRCS})
|
||||||
|
target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
|
||||||
|
|
||||||
|
# Change the compile flags for SIMD files we use.
|
||||||
|
list(LENGTH WEBP_SIMD_FILES_TO_INCLUDE WEBP_SIMD_FILES_TO_INCLUDE_LENGTH)
|
||||||
|
math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE
|
||||||
|
"${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE})
|
||||||
|
list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE)
|
||||||
|
list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG)
|
||||||
|
set_source_files_properties(${FILE} PROPERTIES
|
||||||
|
COMPILE_FLAGS ${SIMD_COMPILE_FLAG}
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Build the executables if asked for.
|
||||||
|
if(WEBP_BUILD_CWEBP OR WEBP_BUILD_DWEBP OR
|
||||||
|
WEBP_BUILD_GIF2WEBP OR WEBP_BUILD_IMG2WEBP)
|
||||||
|
# Example utility library.
|
||||||
|
set(exampleutil_SRCS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/example_util.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/example_util.h)
|
||||||
|
add_library(exampleutil ${exampleutil_SRCS})
|
||||||
|
target_link_libraries(exampleutil webp ${WEBP_DEP_LIBRARIES})
|
||||||
|
|
||||||
|
set(imageioutil_SRCS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/imageio_util.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/imageio_util.h)
|
||||||
|
add_library(imageioutil ${imageioutil_SRCS})
|
||||||
|
target_link_libraries(imageioutil ${WEBP_DEP_LIBRARIES})
|
||||||
|
|
||||||
|
# Image-decoding utility library.
|
||||||
|
set(imagedec_SRCS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/gifdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/gifdec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_dec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_dec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/jpegdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/jpegdec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/metadata.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/metadata.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/pngdec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/tiffdec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/webpdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/webpdec.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/wicdec.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/wicdec.h)
|
||||||
|
add_library(imagedec ${imagedec_SRCS})
|
||||||
|
target_link_libraries(imagedec webp ${WEBP_DEP_LIBRARIES}
|
||||||
|
${WEBP_DEP_IMG_LIBRARIES})
|
||||||
|
|
||||||
|
# Image-encoding utility library.
|
||||||
|
set(imageenc_SRCS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/imageio/image_enc.h)
|
||||||
|
add_library(imageenc ${imageenc_SRCS})
|
||||||
|
target_link_libraries(imageenc webp imageioutil
|
||||||
|
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WEBP_BUILD_DWEBP)
|
||||||
|
# dwebp
|
||||||
|
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
|
||||||
|
add_executable(dwebp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/dwebp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
|
||||||
|
target_link_libraries(dwebp imagedec imageenc webp
|
||||||
|
exampleutil imageioutil
|
||||||
|
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WEBP_BUILD_CWEBP)
|
||||||
|
# cwebp
|
||||||
|
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
|
||||||
|
add_executable(cwebp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/cwebp.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
|
||||||
|
target_link_libraries(cwebp imagedec webp exampleutil imageioutil
|
||||||
|
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WEBP_BUILD_GIF2WEBP)
|
||||||
|
# gif2webp
|
||||||
|
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
|
||||||
|
set(GIF2WEBP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/gif2webp.c)
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "GIF2WEBP_SRCS")
|
||||||
|
add_executable(gif2webp ${GIF2WEBP_SRCS})
|
||||||
|
target_link_libraries(gif2webp imagedec webp exampleutil imageioutil
|
||||||
|
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WEBP_BUILD_IMG2WEBP)
|
||||||
|
# img2webp
|
||||||
|
include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
|
||||||
|
set(IMG2WEBP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/img2webp.c)
|
||||||
|
parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "IMG2WEBP_SRCS")
|
||||||
|
add_executable(img2webp ${IMG2WEBP_SRCS})
|
||||||
|
target_link_libraries(img2webp imagedec webp exampleutil imageioutil
|
||||||
|
${WEBP_DEP_LIBRARIES} ${WEBP_DEP_IMG_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
SUBDIRS = src examples man
|
SUBDIRS = src imageio man
|
||||||
EXTRA_DIST = COPYING autogen.sh
|
EXTRA_DIST = COPYING autogen.sh
|
||||||
|
|
||||||
|
if WANT_EXTRAS
|
||||||
|
SUBDIRS += extras
|
||||||
|
endif
|
||||||
|
|
||||||
|
SUBDIRS += examples
|
||||||
|
|||||||
281
Makefile.vc
281
Makefile.vc
@@ -11,6 +11,8 @@ LIBWEBPDEMUX_BASENAME = libwebpdemux
|
|||||||
ARCH = x86
|
ARCH = x86
|
||||||
!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
|
!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
|
||||||
ARCH = x64
|
ARCH = x64
|
||||||
|
!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ]
|
||||||
|
ARCH = ARM
|
||||||
!ELSE
|
!ELSE
|
||||||
!ERROR Unable to auto-detect toolchain architecture! \
|
!ERROR Unable to auto-detect toolchain architecture! \
|
||||||
If cl.exe is in your PATH rerun nmake with ARCH=<arch>.
|
If cl.exe is in your PATH rerun nmake with ARCH=<arch>.
|
||||||
@@ -27,15 +29,22 @@ PLATFORM_LDFLAGS = /SAFESEH
|
|||||||
NOLOGO = /nologo
|
NOLOGO = /nologo
|
||||||
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
|
CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
|
||||||
CCDEBUG = cl.exe $(NOLOGO) /Od /Gm /Zi /D_DEBUG /RTC1
|
CCDEBUG = cl.exe $(NOLOGO) /Od /Gm /Zi /D_DEBUG /RTC1
|
||||||
CFLAGS = /Isrc $(NOLOGO) /W3 /EHsc /c /GS
|
CFLAGS = /Isrc $(NOLOGO) /W3 /EHsc /c
|
||||||
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
CFLAGS = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
|
||||||
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
|
|
||||||
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
|
LDFLAGS = /LARGEADDRESSAWARE /MANIFEST /NXCOMPAT /DYNAMICBASE
|
||||||
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
LDFLAGS = $(LDFLAGS) $(PLATFORM_LDFLAGS)
|
||||||
LNKDLL = link.exe /DLL $(NOLOGO)
|
LNKDLL = link.exe /DLL $(NOLOGO)
|
||||||
LNKEXE = link.exe $(NOLOGO)
|
LNKEXE = link.exe $(NOLOGO)
|
||||||
LNKLIB = lib.exe $(NOLOGO)
|
LNKLIB = lib.exe $(NOLOGO)
|
||||||
MT = mt.exe $(NOLOGO)
|
MT = mt.exe $(NOLOGO)
|
||||||
|
RCNODBG = rc.exe $(NOLOGO) /l"0x0409" # 0x409 = U.S. English
|
||||||
|
RCDEBUG = $(RCNODBG) /D_DEBUG
|
||||||
|
|
||||||
|
!IF "$(ARCH)" == "ARM"
|
||||||
|
CFLAGS = $(CFLAGS) /DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP /DWEBP_USE_THREAD
|
||||||
|
!ELSE
|
||||||
|
CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
CFGSET = FALSE
|
CFGSET = FALSE
|
||||||
!IF "$(OBJDIR)" == ""
|
!IF "$(OBJDIR)" == ""
|
||||||
@@ -54,6 +63,11 @@ AVX2_FLAGS = /arch:AVX2
|
|||||||
!IF "$(RTLIBCFG)" == "static"
|
!IF "$(RTLIBCFG)" == "static"
|
||||||
RTLIB = /MT
|
RTLIB = /MT
|
||||||
RTLIBD = /MTd
|
RTLIBD = /MTd
|
||||||
|
!ELSE IF "$(RTLIBCFG)" == "legacy"
|
||||||
|
RTLIBCFG = static
|
||||||
|
RTLIB = /MT
|
||||||
|
RTLIBD = /MTd
|
||||||
|
CFLAGS = $(CFLAGS) /GS- /arch:IA32
|
||||||
!ELSE
|
!ELSE
|
||||||
RTLIB = /MD
|
RTLIB = /MD
|
||||||
RTLIBD = /MDd
|
RTLIBD = /MDd
|
||||||
@@ -70,6 +84,8 @@ OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
|
|||||||
$(DIROBJ)\dsp \
|
$(DIROBJ)\dsp \
|
||||||
$(DIROBJ)\enc \
|
$(DIROBJ)\enc \
|
||||||
$(DIROBJ)\examples \
|
$(DIROBJ)\examples \
|
||||||
|
$(DIROBJ)\extras \
|
||||||
|
$(DIROBJ)\imageio \
|
||||||
$(DIROBJ)\mux \
|
$(DIROBJ)\mux \
|
||||||
$(DIROBJ)\utils \
|
$(DIROBJ)\utils \
|
||||||
|
|
||||||
@@ -87,9 +103,11 @@ LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
|
|||||||
LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
|
LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
|
||||||
!ELSE IF "$(CFG)" == "release-dynamic"
|
!ELSE IF "$(CFG)" == "release-dynamic"
|
||||||
CC = $(CCNODBG)
|
CC = $(CCNODBG)
|
||||||
|
RC = $(RCNODBG)
|
||||||
DLLBUILD = TRUE
|
DLLBUILD = TRUE
|
||||||
!ELSE IF "$(CFG)" == "debug-dynamic"
|
!ELSE IF "$(CFG)" == "debug-dynamic"
|
||||||
CC = $(CCDEBUG)
|
CC = $(CCDEBUG)
|
||||||
|
RC = $(RCDEBUG)
|
||||||
RTLIB = $(RTLIBD)
|
RTLIB = $(RTLIBD)
|
||||||
DLLBUILD = TRUE
|
DLLBUILD = TRUE
|
||||||
LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
|
LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
|
||||||
@@ -106,9 +124,7 @@ LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
|
|||||||
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
|
LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
|
||||||
LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
|
LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
|
||||||
!ELSE IF "$(DLLBUILD)" == "TRUE"
|
!ELSE IF "$(DLLBUILD)" == "TRUE"
|
||||||
DLLC = webp_dll.c
|
|
||||||
DLLINC = webp_dll.h
|
DLLINC = webp_dll.h
|
||||||
DLL_OBJS = $(DIROBJ)\$(DLLC:.c=.obj)
|
|
||||||
CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
|
CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(RTLIB) /DWEBP_DLL
|
||||||
LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
|
LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
|
||||||
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
|
LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
|
||||||
@@ -137,8 +153,11 @@ CFGSET = TRUE
|
|||||||
!MESSAGE . features enabled.
|
!MESSAGE . features enabled.
|
||||||
!MESSAGE - (empty) - build libwebp-based targets for CFG
|
!MESSAGE - (empty) - build libwebp-based targets for CFG
|
||||||
!MESSAGE - all - build (de)mux-based targets for CFG
|
!MESSAGE - all - build (de)mux-based targets for CFG
|
||||||
|
!MESSAGE - gif2webp - requires libgif & >= VS2013
|
||||||
|
!MESSAGE - anim_diff - requires libgif & >= VS2013
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
|
!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
|
||||||
|
!MESSAGE 'legacy' will produce a Windows 2000 compatible library.
|
||||||
!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
|
!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
|
||||||
!MESSAGE defaults to ..\obj
|
!MESSAGE defaults to ..\obj
|
||||||
|
|
||||||
@@ -156,99 +175,160 @@ CFGSET = TRUE
|
|||||||
#
|
#
|
||||||
|
|
||||||
DEC_OBJS = \
|
DEC_OBJS = \
|
||||||
$(DIROBJ)\dec\alpha.obj \
|
$(DIROBJ)\dec\alpha_dec.obj \
|
||||||
$(DIROBJ)\dec\buffer.obj \
|
$(DIROBJ)\dec\buffer_dec.obj \
|
||||||
$(DIROBJ)\dec\frame.obj \
|
$(DIROBJ)\dec\frame_dec.obj \
|
||||||
$(DIROBJ)\dec\idec.obj \
|
$(DIROBJ)\dec\idec_dec.obj \
|
||||||
$(DIROBJ)\dec\io.obj \
|
$(DIROBJ)\dec\io_dec.obj \
|
||||||
$(DIROBJ)\dec\quant.obj \
|
$(DIROBJ)\dec\quant_dec.obj \
|
||||||
$(DIROBJ)\dec\tree.obj \
|
$(DIROBJ)\dec\tree_dec.obj \
|
||||||
$(DIROBJ)\dec\vp8.obj \
|
$(DIROBJ)\dec\vp8_dec.obj \
|
||||||
$(DIROBJ)\dec\vp8l.obj \
|
$(DIROBJ)\dec\vp8l_dec.obj \
|
||||||
$(DIROBJ)\dec\webp.obj \
|
$(DIROBJ)\dec\webp_dec.obj \
|
||||||
|
|
||||||
DEMUX_OBJS = \
|
DEMUX_OBJS = \
|
||||||
|
$(DIROBJ)\demux\anim_decode.obj \
|
||||||
$(DIROBJ)\demux\demux.obj \
|
$(DIROBJ)\demux\demux.obj \
|
||||||
|
|
||||||
DSP_DEC_OBJS = \
|
DSP_DEC_OBJS = \
|
||||||
$(DIROBJ)\dsp\alpha_processing.obj \
|
$(DIROBJ)\dsp\alpha_processing.obj \
|
||||||
|
$(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\alpha_processing_neon.obj \
|
||||||
|
$(DIROBJ)\dsp\alpha_processing_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\alpha_processing_sse41.obj \
|
||||||
$(DIROBJ)\dsp\cpu.obj \
|
$(DIROBJ)\dsp\cpu.obj \
|
||||||
$(DIROBJ)\dsp\dec.obj \
|
$(DIROBJ)\dsp\dec.obj \
|
||||||
$(DIROBJ)\dsp\dec_clip_tables.obj \
|
$(DIROBJ)\dsp\dec_clip_tables.obj \
|
||||||
$(DIROBJ)\dsp\dec_mips32.obj \
|
$(DIROBJ)\dsp\dec_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\dec_msa.obj \
|
||||||
$(DIROBJ)\dsp\dec_neon.obj \
|
$(DIROBJ)\dsp\dec_neon.obj \
|
||||||
$(DIROBJ)\dsp\dec_sse2.obj \
|
$(DIROBJ)\dsp\dec_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\dec_sse41.obj \
|
||||||
|
$(DIROBJ)\dsp\filters.obj \
|
||||||
|
$(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\filters_msa.obj \
|
||||||
|
$(DIROBJ)\dsp\filters_neon.obj \
|
||||||
|
$(DIROBJ)\dsp\filters_sse2.obj \
|
||||||
$(DIROBJ)\dsp\lossless.obj \
|
$(DIROBJ)\dsp\lossless.obj \
|
||||||
$(DIROBJ)\dsp\lossless_mips32.obj \
|
$(DIROBJ)\dsp\lossless_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_msa.obj \
|
||||||
$(DIROBJ)\dsp\lossless_neon.obj \
|
$(DIROBJ)\dsp\lossless_neon.obj \
|
||||||
$(DIROBJ)\dsp\lossless_sse2.obj \
|
$(DIROBJ)\dsp\lossless_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_msa.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_neon.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_sse2.obj \
|
||||||
$(DIROBJ)\dsp\upsampling.obj \
|
$(DIROBJ)\dsp\upsampling.obj \
|
||||||
|
$(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\upsampling_msa.obj \
|
||||||
$(DIROBJ)\dsp\upsampling_neon.obj \
|
$(DIROBJ)\dsp\upsampling_neon.obj \
|
||||||
$(DIROBJ)\dsp\upsampling_sse2.obj \
|
$(DIROBJ)\dsp\upsampling_sse2.obj \
|
||||||
$(DIROBJ)\dsp\yuv.obj \
|
$(DIROBJ)\dsp\yuv.obj \
|
||||||
$(DIROBJ)\dsp\yuv_mips32.obj \
|
$(DIROBJ)\dsp\yuv_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\yuv_mips_dsp_r2.obj \
|
||||||
$(DIROBJ)\dsp\yuv_sse2.obj \
|
$(DIROBJ)\dsp\yuv_sse2.obj \
|
||||||
|
|
||||||
DSP_ENC_OBJS = \
|
DSP_ENC_OBJS = \
|
||||||
|
$(DIROBJ)\dsp\argb.obj \
|
||||||
|
$(DIROBJ)\dsp\argb_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\argb_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\cost.obj \
|
||||||
|
$(DIROBJ)\dsp\cost_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\cost_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\cost_sse2.obj \
|
||||||
$(DIROBJ)\dsp\enc.obj \
|
$(DIROBJ)\dsp\enc.obj \
|
||||||
$(DIROBJ)\dsp\enc_avx2.obj \
|
$(DIROBJ)\dsp\enc_avx2.obj \
|
||||||
$(DIROBJ)\dsp\enc_mips32.obj \
|
$(DIROBJ)\dsp\enc_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\enc_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\enc_msa.obj \
|
||||||
$(DIROBJ)\dsp\enc_neon.obj \
|
$(DIROBJ)\dsp\enc_neon.obj \
|
||||||
$(DIROBJ)\dsp\enc_sse2.obj \
|
$(DIROBJ)\dsp\enc_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\enc_sse41.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_mips32.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_msa.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_neon.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\lossless_enc_sse41.obj \
|
||||||
|
|
||||||
EX_FORMAT_DEC_OBJS = \
|
EX_ANIM_UTIL_OBJS = \
|
||||||
$(DIROBJ)\examples\jpegdec.obj \
|
$(DIROBJ)\examples\anim_util.obj \
|
||||||
$(DIROBJ)\examples\metadata.obj \
|
|
||||||
$(DIROBJ)\examples\pngdec.obj \
|
IMAGEIO_DEC_OBJS = \
|
||||||
$(DIROBJ)\examples\tiffdec.obj \
|
$(DIROBJ)\imageio\image_dec.obj \
|
||||||
$(DIROBJ)\examples\webpdec.obj \
|
$(DIROBJ)\imageio\jpegdec.obj \
|
||||||
$(DIROBJ)\examples\wicdec.obj \
|
$(DIROBJ)\imageio\metadata.obj \
|
||||||
|
$(DIROBJ)\imageio\pngdec.obj \
|
||||||
|
$(DIROBJ)\imageio\tiffdec.obj \
|
||||||
|
$(DIROBJ)\imageio\webpdec.obj \
|
||||||
|
$(DIROBJ)\imageio\wicdec.obj \
|
||||||
|
|
||||||
|
IMAGEIO_ENC_OBJS = \
|
||||||
|
$(DIROBJ)\imageio\image_enc.obj \
|
||||||
|
|
||||||
|
EX_GIF_DEC_OBJS = \
|
||||||
|
$(DIROBJ)\examples\gifdec.obj \
|
||||||
|
|
||||||
EX_UTIL_OBJS = \
|
EX_UTIL_OBJS = \
|
||||||
$(DIROBJ)\examples\example_util.obj \
|
$(DIROBJ)\examples\example_util.obj \
|
||||||
|
|
||||||
ENC_OBJS = \
|
ENC_OBJS = \
|
||||||
$(DIROBJ)\enc\alpha.obj \
|
$(DIROBJ)\enc\alpha_enc.obj \
|
||||||
$(DIROBJ)\enc\analysis.obj \
|
$(DIROBJ)\enc\analysis_enc.obj \
|
||||||
$(DIROBJ)\enc\backward_references.obj \
|
$(DIROBJ)\enc\backward_references_enc.obj \
|
||||||
$(DIROBJ)\enc\config.obj \
|
$(DIROBJ)\enc\config_enc.obj \
|
||||||
$(DIROBJ)\enc\cost.obj \
|
$(DIROBJ)\enc\cost_enc.obj \
|
||||||
$(DIROBJ)\enc\filter.obj \
|
$(DIROBJ)\enc\delta_palettization_enc.obj \
|
||||||
$(DIROBJ)\enc\frame.obj \
|
$(DIROBJ)\enc\filter_enc.obj \
|
||||||
$(DIROBJ)\enc\histogram.obj \
|
$(DIROBJ)\enc\frame_enc.obj \
|
||||||
$(DIROBJ)\enc\iterator.obj \
|
$(DIROBJ)\enc\histogram_enc.obj \
|
||||||
$(DIROBJ)\enc\picture.obj \
|
$(DIROBJ)\enc\iterator_enc.obj \
|
||||||
$(DIROBJ)\enc\picture_csp.obj \
|
$(DIROBJ)\enc\near_lossless_enc.obj \
|
||||||
$(DIROBJ)\enc\picture_psnr.obj \
|
$(DIROBJ)\enc\picture_enc.obj \
|
||||||
$(DIROBJ)\enc\picture_rescale.obj \
|
$(DIROBJ)\enc\picture_csp_enc.obj \
|
||||||
$(DIROBJ)\enc\picture_tools.obj \
|
$(DIROBJ)\enc\picture_psnr_enc.obj \
|
||||||
$(DIROBJ)\enc\quant.obj \
|
$(DIROBJ)\enc\picture_rescale_enc.obj \
|
||||||
$(DIROBJ)\enc\syntax.obj \
|
$(DIROBJ)\enc\picture_tools_enc.obj \
|
||||||
$(DIROBJ)\enc\token.obj \
|
$(DIROBJ)\enc\predictor_enc.obj \
|
||||||
$(DIROBJ)\enc\tree.obj \
|
$(DIROBJ)\enc\quant_enc.obj \
|
||||||
$(DIROBJ)\enc\vp8l.obj \
|
$(DIROBJ)\enc\syntax_enc.obj \
|
||||||
$(DIROBJ)\enc\webpenc.obj \
|
$(DIROBJ)\enc\token_enc.obj \
|
||||||
|
$(DIROBJ)\enc\tree_enc.obj \
|
||||||
|
$(DIROBJ)\enc\vp8l_enc.obj \
|
||||||
|
$(DIROBJ)\enc\webp_enc.obj \
|
||||||
|
|
||||||
|
EXTRAS_OBJS = \
|
||||||
|
$(DIROBJ)\extras\extras.obj \
|
||||||
|
$(DIROBJ)\extras\quality_estimate.obj \
|
||||||
|
|
||||||
|
IMAGEIO_UTIL_OBJS = \
|
||||||
|
$(DIROBJ)\imageio\imageio_util.obj \
|
||||||
|
|
||||||
MUX_OBJS = \
|
MUX_OBJS = \
|
||||||
|
$(DIROBJ)\mux\anim_encode.obj \
|
||||||
$(DIROBJ)\mux\muxedit.obj \
|
$(DIROBJ)\mux\muxedit.obj \
|
||||||
$(DIROBJ)\mux\muxinternal.obj \
|
$(DIROBJ)\mux\muxinternal.obj \
|
||||||
$(DIROBJ)\mux\muxread.obj \
|
$(DIROBJ)\mux\muxread.obj \
|
||||||
|
|
||||||
UTILS_DEC_OBJS = \
|
UTILS_DEC_OBJS = \
|
||||||
$(DIROBJ)\utils\bit_reader.obj \
|
$(DIROBJ)\utils\bit_reader_utils.obj \
|
||||||
$(DIROBJ)\utils\color_cache.obj \
|
$(DIROBJ)\utils\color_cache_utils.obj \
|
||||||
$(DIROBJ)\utils\filters.obj \
|
$(DIROBJ)\utils\filters_utils.obj \
|
||||||
$(DIROBJ)\utils\huffman.obj \
|
$(DIROBJ)\utils\huffman_utils.obj \
|
||||||
$(DIROBJ)\utils\quant_levels_dec.obj \
|
$(DIROBJ)\utils\quant_levels_dec_utils.obj \
|
||||||
$(DIROBJ)\utils\rescaler.obj \
|
$(DIROBJ)\utils\rescaler_utils.obj \
|
||||||
$(DIROBJ)\utils\random.obj \
|
$(DIROBJ)\utils\random_utils.obj \
|
||||||
$(DIROBJ)\utils\thread.obj \
|
$(DIROBJ)\utils\thread_utils.obj \
|
||||||
$(DIROBJ)\utils\utils.obj \
|
$(DIROBJ)\utils\utils.obj \
|
||||||
|
|
||||||
UTILS_ENC_OBJS = \
|
UTILS_ENC_OBJS = \
|
||||||
$(DIROBJ)\utils\bit_writer.obj \
|
$(DIROBJ)\utils\bit_writer_utils.obj \
|
||||||
$(DIROBJ)\utils\huffman_encode.obj \
|
$(DIROBJ)\utils\huffman_encode_utils.obj \
|
||||||
$(DIROBJ)\utils\quant_levels.obj \
|
$(DIROBJ)\utils\quant_levels_utils.obj \
|
||||||
|
|
||||||
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
|
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
|
||||||
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
|
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
|
||||||
@@ -257,19 +337,51 @@ LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
|
|||||||
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
|
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
|
||||||
|
|
||||||
OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
|
OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
|
||||||
|
!IF "$(ARCH)" == "ARM"
|
||||||
|
ex: $(OUT_LIBS)
|
||||||
|
all: ex
|
||||||
|
!ELSE
|
||||||
OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
|
OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
|
||||||
EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe
|
EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe \
|
||||||
|
$(DIRBIN)\img2webp.exe $(DIRBIN)\get_disto.exe \
|
||||||
|
$(DIRBIN)\webp_quality.exe
|
||||||
|
|
||||||
ex: $(OUT_LIBS) $(OUT_EXAMPLES)
|
ex: $(OUT_LIBS) $(OUT_EXAMPLES)
|
||||||
all: ex $(EXTRA_EXAMPLES)
|
all: ex $(EXTRA_EXAMPLES)
|
||||||
$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(EX_FORMAT_DEC_OBJS)
|
# NB: gif2webp.exe and anim_diff.exe are excluded from 'all' as libgif requires
|
||||||
$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj
|
# C99 support which is only available from VS2013 onward.
|
||||||
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj
|
gif2webp: $(DIRBIN)\gif2webp.exe
|
||||||
$(DIRBIN)\vwebp.exe: $(EX_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
|
anim_diff: $(DIRBIN)\anim_diff.exe
|
||||||
|
|
||||||
|
$(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||||
|
$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS)
|
||||||
|
$(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
|
||||||
|
$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
|
||||||
|
$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
|
||||||
|
$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
|
||||||
|
$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
|
||||||
|
$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
|
||||||
$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
|
$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
|
||||||
$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(LIBWEBP)
|
$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
|
||||||
|
$(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX)
|
||||||
|
$(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS)
|
||||||
|
$(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
|
||||||
|
$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
|
||||||
|
$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
|
||||||
|
|
||||||
|
$(DIRBIN)\webp_quality.exe: $(DIROBJ)\extras\webp_quality.obj
|
||||||
|
$(DIRBIN)\webp_quality.exe: $(IMAGEIO_UTIL_OBJS)
|
||||||
|
$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS) $(LIBWEBP)
|
||||||
|
|
||||||
$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
|
$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
|
||||||
$(EX_UTIL_OBJS) $(EX_FORMAT_DEC_OBJS): $(OUTPUT_DIRS)
|
$(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS)
|
||||||
|
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
|
||||||
|
!ENDIF # ARCH == ARM
|
||||||
|
|
||||||
experimental:
|
experimental:
|
||||||
$(MAKE) /f Makefile.vc \
|
$(MAKE) /f Makefile.vc \
|
||||||
@@ -285,20 +397,29 @@ $(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS)
|
|||||||
|
|
||||||
!IF "$(DLLBUILD)" == "TRUE"
|
!IF "$(DLLBUILD)" == "TRUE"
|
||||||
$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
|
$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
|
||||||
$(DIROBJ)\$(DLLINC) $(DIROBJ)\$(DLLC)
|
$(DIROBJ)\$(DLLINC)
|
||||||
|
|
||||||
{$(DIROBJ)}.c{$(DIROBJ)}.obj:
|
{$(DIROBJ)}.c{$(DIROBJ)}.obj:
|
||||||
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
|
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
|
||||||
|
|
||||||
$(LIBWEBPMUX): $(LIBWEBP)
|
{src}.rc{$(DIROBJ)}.res:
|
||||||
$(LIBWEBPDEMUX): $(LIBWEBP)
|
$(RC) /fo$@ $<
|
||||||
|
{src\demux}.rc{$(DIROBJ)\demux}.res:
|
||||||
|
$(RC) /fo$@ $<
|
||||||
|
{src\mux}.rc{$(DIROBJ)\mux}.res:
|
||||||
|
$(RC) /fo$@ $<
|
||||||
|
|
||||||
|
$(LIBWEBP): $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
|
||||||
|
$(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res
|
||||||
|
$(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res
|
||||||
|
$(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res
|
||||||
|
|
||||||
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
|
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
|
||||||
$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
|
$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
|
||||||
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
|
-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@-erase /s $(DIROBJ)\$(DLLC) $(DIROBJ)\$(DLLINC) 2> NUL
|
@-erase /s $(DIROBJ)\$(DLLINC) 2> NUL
|
||||||
!ELSE
|
!ELSE
|
||||||
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
|
$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX):
|
||||||
$(LNKLIB) /out:$@ $**
|
$(LNKLIB) /out:$@ $**
|
||||||
@@ -315,25 +436,31 @@ $(DIROBJ)\$(DLLINC):
|
|||||||
@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@
|
@echo #define WEBP_EXTERN(type) __declspec(dllexport) type >> $@
|
||||||
@echo #endif /* WEBP_DLL_H_ */ >> $@
|
@echo #endif /* WEBP_DLL_H_ */ >> $@
|
||||||
|
|
||||||
# expose a WebPFree() function for use in managed code
|
|
||||||
$(DIROBJ)\$(DLLC): $(DIROBJ)\$(DLLINC)
|
|
||||||
@echo #include ^<stdlib.h^> > $@
|
|
||||||
@echo #include "webp_dll.h" >> $@
|
|
||||||
@echo // This function should be used in place of free() for memory >> $@
|
|
||||||
@echo // returned by the WebP API. >> $@
|
|
||||||
@echo WEBP_EXTERN(void) WebPFree(void* ptr) { >> $@
|
|
||||||
@echo free(ptr); >> $@
|
|
||||||
@echo } >> $@
|
|
||||||
|
|
||||||
.SUFFIXES: .c .obj .res .exe
|
.SUFFIXES: .c .obj .res .exe
|
||||||
# File-specific flag builds. Note batch rules take precedence over wildcards,
|
# File-specific flag builds. Note batch rules take precedence over wildcards,
|
||||||
# so for now name each file individually.
|
# so for now name each file individually.
|
||||||
$(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c
|
$(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c
|
||||||
$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \
|
$(CC) $(CFLAGS) $(AVX2_FLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ \
|
||||||
src\dsp\$(@B).c
|
src\dsp\$(@B).c
|
||||||
|
$(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c
|
||||||
|
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
|
||||||
|
/Fo$(DIROBJ)\examples\ examples\$(@B).c
|
||||||
|
$(DIROBJ)\examples\anim_util.obj: examples\anim_util.c
|
||||||
|
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
|
||||||
|
/Fo$(DIROBJ)\examples\ examples\$(@B).c
|
||||||
|
$(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c
|
||||||
|
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
|
||||||
|
/Fo$(DIROBJ)\examples\ examples\$(@B).c
|
||||||
|
$(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
|
||||||
|
$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
|
||||||
|
/Fo$(DIROBJ)\examples\ examples\$(@B).c
|
||||||
# Batch rules
|
# Batch rules
|
||||||
{examples}.c{$(DIROBJ)\examples}.obj::
|
{examples}.c{$(DIROBJ)\examples}.obj::
|
||||||
$(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
|
$(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
|
||||||
|
{extras}.c{$(DIROBJ)\extras}.obj::
|
||||||
|
$(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
|
||||||
|
{imageio}.c{$(DIROBJ)\imageio}.obj::
|
||||||
|
$(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
|
||||||
{src\dec}.c{$(DIROBJ)\dec}.obj::
|
{src\dec}.c{$(DIROBJ)\dec}.obj::
|
||||||
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
|
$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
|
||||||
{src\demux}.c{$(DIROBJ)\demux}.obj::
|
{src\demux}.c{$(DIROBJ)\demux}.obj::
|
||||||
@@ -353,6 +480,12 @@ $(DIROBJ)\dsp\enc_avx2.obj: src\dsp\enc_avx2.c
|
|||||||
$(MT) -manifest $@.manifest -outputresource:$@;1
|
$(MT) -manifest $@.manifest -outputresource:$@;1
|
||||||
del $@.manifest
|
del $@.manifest
|
||||||
|
|
||||||
|
{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
|
||||||
|
$(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
|
||||||
|
ole32.lib windowscodecs.lib shlwapi.lib
|
||||||
|
$(MT) -manifest $@.manifest -outputresource:$@;1
|
||||||
|
del $@.manifest
|
||||||
|
|
||||||
clean::
|
clean::
|
||||||
@-erase /s $(DIROBJ)\*.dll 2> NUL
|
@-erase /s $(DIROBJ)\*.dll 2> NUL
|
||||||
@-erase /s $(DIROBJ)\*.exp 2> NUL
|
@-erase /s $(DIROBJ)\*.exp 2> NUL
|
||||||
|
|||||||
90
NEWS
90
NEWS
@@ -1,3 +1,90 @@
|
|||||||
|
- 1/26/2017: version 0.6.0
|
||||||
|
* lossless performance and compression improvements
|
||||||
|
* miscellaneous performance improvements (SSE2, NEON, MSA)
|
||||||
|
* webpmux gained a -duration option allowing for frame timing modification
|
||||||
|
* new img2webp utility allowing a sequence of images to be converted to
|
||||||
|
animated webp
|
||||||
|
* API changes:
|
||||||
|
- libwebp:
|
||||||
|
WebPPictureSharpARGBToYUVA
|
||||||
|
WebPPlaneDistortion
|
||||||
|
- libwebpmux / gif2webp:
|
||||||
|
WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1
|
||||||
|
forces all keyframes. See mux.h and the gif2webp
|
||||||
|
manpage for details.
|
||||||
|
|
||||||
|
- 12/13/2016: version 0.5.2
|
||||||
|
This is a binary compatible release.
|
||||||
|
This release covers CVE-2016-8888 and CVE-2016-9085.
|
||||||
|
* further security related hardening in the tools; fixes to
|
||||||
|
gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue
|
||||||
|
#312)
|
||||||
|
* full libwebp (encoder & decoder) iOS framework; libwebpdecoder
|
||||||
|
WebP.framework renamed to WebPDecoder.framework (issue #307)
|
||||||
|
* CMake support for Android Studio (2.2)
|
||||||
|
* miscellaneous build related fixes (issue #306, #313)
|
||||||
|
* miscellaneous documentation improvements (issue #225)
|
||||||
|
* minor lossy encoder fixes and improvements
|
||||||
|
|
||||||
|
- 6/14/2016: version 0.5.1
|
||||||
|
This is a binary compatible release.
|
||||||
|
* miscellaneous bug fixes (issues #280, #289)
|
||||||
|
* reverted alpha plane encoding with color cache for compatibility with
|
||||||
|
libwebp 0.4.0->0.4.3 (issues #291, #298)
|
||||||
|
* lossless encoding performance improvements
|
||||||
|
* memory reduction in both lossless encoding and decoding
|
||||||
|
* force mux output to be in the extended format (VP8X) when undefined chunks
|
||||||
|
are present (issue #294)
|
||||||
|
* gradle, cmake build support
|
||||||
|
* workaround for compiler bug causing 64-bit decode failures on android
|
||||||
|
devices using clang-3.8 in the r11c NDK
|
||||||
|
* various WebPAnimEncoder improvements
|
||||||
|
|
||||||
|
- 12/17/2015: version 0.5.0
|
||||||
|
* miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278)
|
||||||
|
* encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless
|
||||||
|
- note! YUV->RGB conversion was sped-up, but the results will be slightly
|
||||||
|
different from previous releases
|
||||||
|
* various lossless encoder improvements
|
||||||
|
* gif2webp improvements, -min_size option added
|
||||||
|
* tools fully support input from stdin and output to stdout (issue #168)
|
||||||
|
* New WebPAnimEncoder API for creating animations
|
||||||
|
* New WebPAnimDecoder API for decoding animations
|
||||||
|
* other API changes:
|
||||||
|
- libwebp:
|
||||||
|
WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp)
|
||||||
|
WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default)
|
||||||
|
WebPConfig::near_lossless (-near_lossless in cwebp)
|
||||||
|
WebPFree() (free'ing webp allocated memory in other languages)
|
||||||
|
WebPConfigLosslessPreset()
|
||||||
|
WebPMemoryWriterClear()
|
||||||
|
- libwebpdemux: removed experimental fragment related fields and functions
|
||||||
|
- libwebpmux: WebPMuxSetCanvasSize()
|
||||||
|
* new libwebpextras library with some uncommon import functions:
|
||||||
|
WebPImportGray/WebPImportRGB565/WebPImportRGB4444
|
||||||
|
|
||||||
|
- 10/15/15: version 0.4.4
|
||||||
|
This is a binary compatible release.
|
||||||
|
* rescaling out-of-bounds read fix (issue #254)
|
||||||
|
* various build fixes and improvements (issues #253, #259, #262, #267, #268)
|
||||||
|
* container documentation update
|
||||||
|
* gif2webp transparency fix (issue #245)
|
||||||
|
|
||||||
|
- 3/3/15: version 0.4.3
|
||||||
|
This is a binary compatible release.
|
||||||
|
* Android / gcc / iOS / MSVS build fixes and improvements
|
||||||
|
* lossless decode fix (issue #239 -- since 0.4.0)
|
||||||
|
* documentation / vwebp updates for animation
|
||||||
|
* multi-threading fix (issue #234)
|
||||||
|
|
||||||
|
- 10/13/14: version 0.4.2
|
||||||
|
This is a binary compatible release.
|
||||||
|
* Android / gcc build fixes
|
||||||
|
* (Windows) fix reading from stdin and writing to stdout
|
||||||
|
* gif2webp: miscellaneous fixes
|
||||||
|
* fix 'alpha-leak' with lossy compression (issue #220)
|
||||||
|
* the lossless bitstream spec has been amended to reflect the current code
|
||||||
|
|
||||||
- 7/24/14: version 0.4.1
|
- 7/24/14: version 0.4.1
|
||||||
This is a binary compatible release.
|
This is a binary compatible release.
|
||||||
* AArch64 (arm64) & MIPS support/optimizations
|
* AArch64 (arm64) & MIPS support/optimizations
|
||||||
@@ -74,7 +161,8 @@
|
|||||||
- 9/19/11: version 0.1.3
|
- 9/19/11: version 0.1.3
|
||||||
* Advanced decoding APIs.
|
* Advanced decoding APIs.
|
||||||
* On-the-fly cropping and rescaling of images.
|
* On-the-fly cropping and rescaling of images.
|
||||||
* SSE2 instructions for decoding performance optimizations on x86 based platforms.
|
* SSE2 instructions for decoding performance optimizations on x86 based
|
||||||
|
platforms.
|
||||||
* Support Multi-threaded decoding.
|
* Support Multi-threaded decoding.
|
||||||
* 40% improvement in Decoding performance.
|
* 40% improvement in Decoding performance.
|
||||||
* Add support for RGB565, RGBA4444 & ARGB image colorspace.
|
* Add support for RGB565, RGBA4444 & ARGB image colorspace.
|
||||||
|
|||||||
39
PATENTS
39
PATENTS
@@ -1,22 +1,23 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
Additional IP Rights Grant (Patents)
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
"These implementations" means the copyrightable works that implement the WebM
|
||||||
Google as part of the WebM Project.
|
codecs distributed by Google as part of the WebM Project.
|
||||||
|
|
||||||
Google hereby grants to you a perpetual, worldwide, non-exclusive,
|
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
royalty-free, irrevocable (except as stated in this section) patent license to
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
make, have made, use, offer to sell, sell, import, transfer, and otherwise
|
||||||
transfer, and otherwise run, modify and propagate the contents of this
|
run, modify and propagate the contents of these implementations of WebM, where
|
||||||
implementation of VP8, where such license applies only to those patent
|
such license applies only to those patent claims, both currently owned by
|
||||||
claims, both currently owned by Google and acquired in the future,
|
Google and acquired in the future, licensable by Google that are necessarily
|
||||||
licensable by Google that are necessarily infringed by this
|
infringed by these implementations of WebM. This grant does not include claims
|
||||||
implementation of VP8. This grant does not include claims that would be
|
that would be infringed only as a consequence of further modification of these
|
||||||
infringed only as a consequence of further modification of this
|
implementations. If you or your agent or exclusive licensee institute or order
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
or agree to the institution of patent litigation or any other patent
|
||||||
order or agree to the institution of patent litigation against any
|
enforcement activity against any entity (including a cross-claim or
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
counterclaim in a lawsuit) alleging that any of these implementations of WebM
|
||||||
that this implementation of VP8 or any code incorporated within this
|
or any code incorporated within any of these implementations of WebM
|
||||||
implementation of VP8 constitutes direct or contributory patent
|
constitute direct or contributory patent infringement, or inducement of
|
||||||
infringement, or inducement of patent infringement, then any patent
|
patent infringement, then any patent rights granted to you under this License
|
||||||
rights granted to you under this License for this implementation of VP8
|
for these implementations of WebM shall terminate as of the date such
|
||||||
shall terminate as of the date such litigation is filed.
|
litigation is filed.
|
||||||
|
|||||||
191
README
191
README
@@ -4,7 +4,7 @@
|
|||||||
\__\__/\____/\_____/__/ ____ ___
|
\__\__/\____/\_____/__/ ____ ___
|
||||||
/ _/ / \ \ / _ \/ _/
|
/ _/ / \ \ / _ \/ _/
|
||||||
/ \_/ / / \ \ __/ \__
|
/ \_/ / / \ \ __/ \__
|
||||||
\____/____/\_____/_____/____/v0.4.1
|
\____/____/\_____/_____/____/v0.6.0
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
============
|
============
|
||||||
@@ -15,11 +15,12 @@ as well as the command line tools 'cwebp' and 'dwebp'.
|
|||||||
|
|
||||||
See http://developers.google.com/speed/webp
|
See http://developers.google.com/speed/webp
|
||||||
|
|
||||||
Latest sources are available from http://www.webmproject.org/code/
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
|
|
||||||
It is released under the same license as the WebM project.
|
It is released under the same license as the WebM project.
|
||||||
See http://www.webmproject.org/license/software/ or the
|
See http://www.webmproject.org/license/software/ or the
|
||||||
file "COPYING" file for details. An additional intellectual
|
"COPYING" file for details. An additional intellectual
|
||||||
property rights grant can be found in the file PATENTS.
|
property rights grant can be found in the file PATENTS.
|
||||||
|
|
||||||
Building:
|
Building:
|
||||||
@@ -53,6 +54,12 @@ Please refer to makefile.unix for additional details and customizations.
|
|||||||
|
|
||||||
Using autoconf tools:
|
Using autoconf tools:
|
||||||
---------------------
|
---------------------
|
||||||
|
Prerequisites:
|
||||||
|
A compiler (e.g., gcc), make, autoconf, automake, libtool.
|
||||||
|
On a Debian-like system the following should install everything you need for a
|
||||||
|
minimal build:
|
||||||
|
$ sudo apt-get install gcc make autoconf automake libtool
|
||||||
|
|
||||||
When building from git sources, you will need to run autogen.sh to generate the
|
When building from git sources, you will need to run autogen.sh to generate the
|
||||||
configure script.
|
configure script.
|
||||||
|
|
||||||
@@ -77,6 +84,73 @@ be installed independently using a minor modification in the corresponding
|
|||||||
Makefile.am configure files (see comments there). See './configure --help' for
|
Makefile.am configure files (see comments there). See './configure --help' for
|
||||||
more options.
|
more options.
|
||||||
|
|
||||||
|
Building for MIPS Linux:
|
||||||
|
------------------------
|
||||||
|
MIPS Linux toolchain stable available releases can be found at:
|
||||||
|
https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
|
||||||
|
|
||||||
|
# Add toolchain to PATH
|
||||||
|
export PATH=$PATH:/path/to/toolchain/bin
|
||||||
|
|
||||||
|
# 32-bit build for mips32r5 (p5600)
|
||||||
|
HOST=mips-mti-linux-gnu
|
||||||
|
MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
|
||||||
|
-msched-weight -mload-store-pairs -fPIE"
|
||||||
|
MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
|
||||||
|
|
||||||
|
# 64-bit build for mips64r6 (i6400)
|
||||||
|
HOST=mips-img-linux-gnu
|
||||||
|
MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
|
||||||
|
-msched-weight -mload-store-pairs -fPIE"
|
||||||
|
MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
|
||||||
|
|
||||||
|
./configure --host=${HOST} --build=`config.guess` \
|
||||||
|
CC="${HOST}-gcc -EL" \
|
||||||
|
CFLAGS="$MIPS_CFLAGS" \
|
||||||
|
LDFLAGS="$MIPS_LDFLAGS"
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
CMake:
|
||||||
|
------
|
||||||
|
The support for CMake is minimal: it only helps you compile libwebp, cwebp and
|
||||||
|
dwebp.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
A compiler (e.g., gcc with autotools) and CMake.
|
||||||
|
On a Debian-like system the following should install everything you need for a
|
||||||
|
minimal build:
|
||||||
|
$ sudo apt-get install build-essential cmake
|
||||||
|
|
||||||
|
When building from git sources, you will need to run cmake to generate the
|
||||||
|
configure script.
|
||||||
|
|
||||||
|
mkdir build && cd build && cmake ../
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
If you also want cwebp or dwebp, you will need to enable them through CMake:
|
||||||
|
|
||||||
|
cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
|
||||||
|
|
||||||
|
or through your favorite interface (like ccmake or cmake-qt-gui).
|
||||||
|
|
||||||
|
Gradle:
|
||||||
|
-------
|
||||||
|
The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
|
||||||
|
dwebp and webpmux_example.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
A compiler (e.g., gcc with autotools) and gradle.
|
||||||
|
On a Debian-like system the following should install everything you need for a
|
||||||
|
minimal build:
|
||||||
|
$ sudo apt-get install build-essential gradle
|
||||||
|
|
||||||
|
When building from git sources, you will need to run the Gradle wrapper with the
|
||||||
|
appropriate target, e.g. :
|
||||||
|
|
||||||
|
./gradlew buildAllExecutables
|
||||||
|
|
||||||
SWIG bindings:
|
SWIG bindings:
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@@ -144,26 +218,30 @@ If input size (-s) for an image is not specified, it is
|
|||||||
assumed to be a PNG, JPEG, TIFF or WebP file.
|
assumed to be a PNG, JPEG, TIFF or WebP file.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h / -help ............ short help
|
-h / -help ............. short help
|
||||||
-H / -longhelp ........ long help
|
-H / -longhelp ......... long help
|
||||||
-q <float> ............. quality factor (0:small..100:big)
|
-q <float> ............. quality factor (0:small..100:big), default=75
|
||||||
-alpha_q <int> ......... transparency-compression quality (0..100)
|
-alpha_q <int> ......... transparency-compression quality (0..100),
|
||||||
|
default=100
|
||||||
-preset <string> ....... preset setting, one of:
|
-preset <string> ....... preset setting, one of:
|
||||||
default, photo, picture,
|
default, photo, picture,
|
||||||
drawing, icon, text
|
drawing, icon, text
|
||||||
-preset must come first, as it overwrites other parameters
|
-preset must come first, as it overwrites other parameters
|
||||||
|
-z <int> ............... activates lossless preset with given
|
||||||
|
level in [0:fast, ..., 9:slowest]
|
||||||
|
|
||||||
-m <int> ............... compression method (0=fast, 6=slowest)
|
-m <int> ............... compression method (0=fast, 6=slowest), default=4
|
||||||
-segments <int> ........ number of segments to use (1..4)
|
-segments <int> ........ number of segments to use (1..4), default=4
|
||||||
-size <int> ............ target size (in bytes)
|
-size <int> ............ target size (in bytes)
|
||||||
-psnr <float> .......... target PSNR (in dB. typically: 42)
|
-psnr <float> .......... target PSNR (in dB. typically: 42)
|
||||||
|
|
||||||
-s <int> <int> ......... input size (width x height) for YUV
|
-s <int> <int> ......... input size (width x height) for YUV
|
||||||
-sns <int> ............. spatial noise shaping (0:off, 100:max)
|
-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
|
||||||
-f <int> ............... filter strength (0=off..100)
|
-f <int> ............... filter strength (0=off..100), default=60
|
||||||
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp)
|
-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
|
||||||
-strong ................ use strong filter instead of simple (default)
|
-strong ................ use strong filter instead of simple (default)
|
||||||
-nostrong .............. use simple filter instead of strong
|
-nostrong .............. use simple filter instead of strong
|
||||||
|
-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
|
||||||
-partition_limit <int> . limit quality to fit the 512k limit on
|
-partition_limit <int> . limit quality to fit the 512k limit on
|
||||||
the first partition (0=no degradation ... 100=full)
|
the first partition (0=no degradation ... 100=full)
|
||||||
-pass <int> ............ analysis pass number (1..10)
|
-pass <int> ............ analysis pass number (1..10)
|
||||||
@@ -176,16 +254,18 @@ Options:
|
|||||||
-print_ssim ............ prints averaged SSIM distortion
|
-print_ssim ............ prints averaged SSIM distortion
|
||||||
-print_lsim ............ prints local-similarity distortion
|
-print_lsim ............ prints local-similarity distortion
|
||||||
-d <file.pgm> .......... dump the compressed output (PGM file)
|
-d <file.pgm> .......... dump the compressed output (PGM file)
|
||||||
-alpha_method <int> .... transparency-compression method (0..1)
|
-alpha_method <int> .... transparency-compression method (0..1), default=1
|
||||||
-alpha_filter <string> . predictive filtering for alpha plane,
|
-alpha_filter <string> . predictive filtering for alpha plane,
|
||||||
one of: none, fast (default) or best
|
one of: none, fast (default) or best
|
||||||
-alpha_cleanup ......... clean RGB values in transparent area
|
-exact ................. preserve RGB values in transparent area, default=off
|
||||||
-blend_alpha <hex> ..... blend colors against background color
|
-blend_alpha <hex> ..... blend colors against background color
|
||||||
expressed as RGB values written in
|
expressed as RGB values written in
|
||||||
hexadecimal, e.g. 0xc0e0d0 for red=0xc0
|
hexadecimal, e.g. 0xc0e0d0 for red=0xc0
|
||||||
green=0xe0 and blue=0xd0
|
green=0xe0 and blue=0xd0
|
||||||
-noalpha ............... discard any transparency information
|
-noalpha ............... discard any transparency information
|
||||||
-lossless .............. encode image losslessly
|
-lossless .............. encode image losslessly, default=off
|
||||||
|
-near_lossless <int> ... use near-lossless image
|
||||||
|
preprocessing (0..100=off), default=100
|
||||||
-hint <string> ......... specify image characteristics hint,
|
-hint <string> ......... specify image characteristics hint,
|
||||||
one of: photo, picture or graph
|
one of: photo, picture or graph
|
||||||
|
|
||||||
@@ -263,18 +343,21 @@ Use following options to convert into alternate image formats:
|
|||||||
-yuv ......... save the raw YUV samples in flat layout
|
-yuv ......... save the raw YUV samples in flat layout
|
||||||
|
|
||||||
Other options are:
|
Other options are:
|
||||||
-version .... print version number and exit
|
-version ..... print version number and exit
|
||||||
-nofancy ..... don't use the fancy YUV420 upscaler
|
-nofancy ..... don't use the fancy YUV420 upscaler
|
||||||
-nofilter .... disable in-loop filtering
|
-nofilter .... disable in-loop filtering
|
||||||
-nodither .... disable dithering
|
-nodither .... disable dithering
|
||||||
-dither <d> .. dithering strength (in 0..100)
|
-dither <d> .. dithering strength (in 0..100)
|
||||||
|
-alpha_dither use alpha-plane dithering if needed
|
||||||
-mt .......... use multi-threading
|
-mt .......... use multi-threading
|
||||||
-crop <x> <y> <w> <h> ... crop output with the given rectangle
|
-crop <x> <y> <w> <h> ... crop output with the given rectangle
|
||||||
-scale <w> <h> .......... scale the output (*after* any cropping)
|
-resize <w> <h> ......... scale the output (*after* any cropping)
|
||||||
|
-flip ........ flip the output vertically
|
||||||
-alpha ....... only save the alpha plane
|
-alpha ....... only save the alpha plane
|
||||||
-incremental . use incremental decoding (useful for tests)
|
-incremental . use incremental decoding (useful for tests)
|
||||||
-h ....... this help message
|
-h ........... this help message
|
||||||
-v ....... verbose (e.g. print encoding/decoding times)
|
-v ........... verbose (e.g. print encoding/decoding times)
|
||||||
|
-quiet ....... quiet mode, don't print anything
|
||||||
-noasm ....... disable all assembly optimizations
|
-noasm ....... disable all assembly optimizations
|
||||||
|
|
||||||
Visualization tool:
|
Visualization tool:
|
||||||
@@ -289,18 +372,20 @@ Usage: vwebp in_file [options]
|
|||||||
|
|
||||||
Decodes the WebP image file and visualize it using OpenGL
|
Decodes the WebP image file and visualize it using OpenGL
|
||||||
Options are:
|
Options are:
|
||||||
-version .... print version number and exit
|
-version ..... print version number and exit
|
||||||
-noicc ....... don't use the icc profile if present
|
-noicc ....... don't use the icc profile if present
|
||||||
-nofancy ..... don't use the fancy YUV420 upscaler
|
-nofancy ..... don't use the fancy YUV420 upscaler
|
||||||
-nofilter .... disable in-loop filtering
|
-nofilter .... disable in-loop filtering
|
||||||
-dither <int> dithering strength (0..100), default=50
|
-dither <int> dithering strength (0..100), default=50
|
||||||
|
-noalphadither disable alpha plane dithering
|
||||||
-mt .......... use multi-threading
|
-mt .......... use multi-threading
|
||||||
-info ........ print info
|
-info ........ print info
|
||||||
-h ....... this help message
|
-h ........... this help message
|
||||||
|
|
||||||
Keyboard shortcuts:
|
Keyboard shortcuts:
|
||||||
'c' ................ toggle use of color profile
|
'c' ................ toggle use of color profile
|
||||||
'i' ................ overlay file information
|
'i' ................ overlay file information
|
||||||
|
'd' ................ disable blending & disposal (debug)
|
||||||
'q' / 'Q' / ESC .... quit
|
'q' / 'Q' / ESC .... quit
|
||||||
|
|
||||||
Building:
|
Building:
|
||||||
@@ -329,6 +414,37 @@ $ make -f makefile.unix examples/vwebp
|
|||||||
> nmake /f Makefile.vc CFG=release-static \
|
> nmake /f Makefile.vc CFG=release-static \
|
||||||
../obj/x64/release-static/bin/vwebp.exe
|
../obj/x64/release-static/bin/vwebp.exe
|
||||||
|
|
||||||
|
Animation creation tool:
|
||||||
|
========================
|
||||||
|
The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...)
|
||||||
|
into an animated WebP file. It offers fine control over duration, encoding
|
||||||
|
modes, etc.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
img2webp [file-level options] [image files...] [per-frame options...]
|
||||||
|
|
||||||
|
File-level options (only used at the start of compression):
|
||||||
|
-min_size ............ minimize size
|
||||||
|
-loop <int> .......... loop count (default: 0, = infinite loop)
|
||||||
|
-kmax <int> .......... maximum number of frame between key-frames
|
||||||
|
(0=only keyframes)
|
||||||
|
-kmin <int> .......... minimum number of frame between key-frames
|
||||||
|
(0=disable key-frames altogether)
|
||||||
|
-mixed ............... use mixed lossy/lossless automatic mode
|
||||||
|
-v ................... verbose mode
|
||||||
|
-h ................... this help
|
||||||
|
|
||||||
|
Per-frame options (only used for subsequent images input):
|
||||||
|
-d <int> ............. frame duration in ms (default: 100)
|
||||||
|
-lossless ........... use lossless mode (default)
|
||||||
|
-lossy ... ........... use lossy mode
|
||||||
|
-q <float> ........... quality
|
||||||
|
-m <int> ............. method to use
|
||||||
|
|
||||||
|
example: img2webp -loop 2 in0.png -lossy in1.jpg
|
||||||
|
-d 80 in2.tiff -o out.webp
|
||||||
|
|
||||||
Animated GIF conversion:
|
Animated GIF conversion:
|
||||||
========================
|
========================
|
||||||
Animated GIF files can be converted to WebP files with animation using the
|
Animated GIF files can be converted to WebP files with animation using the
|
||||||
@@ -338,12 +454,16 @@ vwebp.
|
|||||||
Usage:
|
Usage:
|
||||||
gif2webp [options] gif_file -o webp_file
|
gif2webp [options] gif_file -o webp_file
|
||||||
Options:
|
Options:
|
||||||
-h / -help ............ this help
|
-h / -help ............. this help
|
||||||
-lossy ................. encode image using lossy compression
|
-lossy ................. encode image using lossy compression
|
||||||
-mixed ................. for each frame in the image, pick lossy
|
-mixed ................. for each frame in the image, pick lossy
|
||||||
or lossless compression heuristically
|
or lossless compression heuristically
|
||||||
-q <float> ............. quality factor (0:small..100:big)
|
-q <float> ............. quality factor (0:small..100:big)
|
||||||
-m <int> ............... compression method (0=fast, 6=slowest)
|
-m <int> ............... compression method (0=fast, 6=slowest)
|
||||||
|
-min_size .............. minimize output size (default:off)
|
||||||
|
lossless compression by default; can be
|
||||||
|
combined with -q, -m, -lossy or -mixed
|
||||||
|
options
|
||||||
-kmin <int> ............ min distance between key frames
|
-kmin <int> ............ min distance between key frames
|
||||||
-kmax <int> ............ max distance between key frames
|
-kmax <int> ............ max distance between key frames
|
||||||
-f <int> ............... filter strength (0=off..100)
|
-f <int> ............... filter strength (0=off..100)
|
||||||
@@ -366,6 +486,29 @@ or using autoconf:
|
|||||||
$ ./configure --enable-everything
|
$ ./configure --enable-everything
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
|
Comparison of animated images:
|
||||||
|
==============================
|
||||||
|
Test utility anim_diff under examples/ can be used to compare two animated
|
||||||
|
images (each can be GIF or WebP).
|
||||||
|
|
||||||
|
Usage: anim_diff <image1> <image2> [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-dump_frames <folder> dump decoded frames in PAM format
|
||||||
|
-min_psnr <float> ... minimum per-frame PSNR
|
||||||
|
-raw_comparison ..... if this flag is not used, RGB is
|
||||||
|
premultiplied before comparison
|
||||||
|
|
||||||
|
Building:
|
||||||
|
---------
|
||||||
|
With the libgif development files and a C++ compiler installed, anim_diff can
|
||||||
|
be built using makefile.unix:
|
||||||
|
$ make -f makefile.unix examples/anim_diff
|
||||||
|
|
||||||
|
or using autoconf:
|
||||||
|
$ ./configure --enable-everything
|
||||||
|
$ make
|
||||||
|
|
||||||
Encoding API:
|
Encoding API:
|
||||||
=============
|
=============
|
||||||
|
|
||||||
@@ -595,8 +738,8 @@ an otherwise too-large picture. Some CPU can be saved too, incidentally.
|
|||||||
Bugs:
|
Bugs:
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting-patches/
|
||||||
|
|
||||||
|
|||||||
45
README.mux
45
README.mux
@@ -1,7 +1,7 @@
|
|||||||
__ __ ____ ____ ____ __ __ _ __ __
|
__ __ ____ ____ ____ __ __ _ __ __
|
||||||
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
|
/ \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
|
||||||
\ / __/ _ \ __/ / / (_/ /__
|
\ / __/ _ \ __/ / / (_/ /__
|
||||||
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.2.1
|
\__\__/\_____/_____/__/ \__//_/\_____/__/___/v0.4.0
|
||||||
|
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
@@ -25,6 +25,8 @@ A list of options is available using the -help command line flag:
|
|||||||
> webpmux -help
|
> webpmux -help
|
||||||
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
|
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
|
||||||
webpmux -set SET_OPTIONS INPUT -o OUTPUT
|
webpmux -set SET_OPTIONS INPUT -o OUTPUT
|
||||||
|
webpmux -duration DURATION_OPTIONS [-duration ...]
|
||||||
|
INPUT -o OUTPUT
|
||||||
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
|
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
|
||||||
webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
|
webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
|
||||||
[-bgcolor BACKGROUND_COLOR] -o OUTPUT
|
[-bgcolor BACKGROUND_COLOR] -o OUTPUT
|
||||||
@@ -48,6 +50,17 @@ SET_OPTIONS:
|
|||||||
'file.exif' contains the EXIF metadata to be set
|
'file.exif' contains the EXIF metadata to be set
|
||||||
'file.xmp' contains the XMP metadata to be set
|
'file.xmp' contains the XMP metadata to be set
|
||||||
|
|
||||||
|
DURATION_OPTIONS:
|
||||||
|
Set duration of selected frames:
|
||||||
|
duration set duration for each frames
|
||||||
|
duration,frame set duration of a particular frame
|
||||||
|
duration,start,end set duration of frames in the
|
||||||
|
interval [start,end])
|
||||||
|
where: 'duration' is the duration in milliseconds
|
||||||
|
'start' is the start frame index
|
||||||
|
'end' is the inclusive end frame index
|
||||||
|
The special 'end' value '0' means: last frame.
|
||||||
|
|
||||||
STRIP_OPTIONS:
|
STRIP_OPTIONS:
|
||||||
Strip color profile/metadata:
|
Strip color profile/metadata:
|
||||||
icc strip ICC profile
|
icc strip ICC profile
|
||||||
@@ -133,7 +146,7 @@ WebP files. This API currently supports reading of XMP/EXIF metadata, ICC
|
|||||||
profile and animated images. Other features may be added in subsequent
|
profile and animated images. Other features may be added in subsequent
|
||||||
releases.
|
releases.
|
||||||
|
|
||||||
Code Example: Demuxing WebP data to extract all the frames, ICC profile
|
Code example: Demuxing WebP data to extract all the frames, ICC profile
|
||||||
and EXIF/XMP metadata.
|
and EXIF/XMP metadata.
|
||||||
|
|
||||||
WebPDemuxer* demux = WebPDemux(&webp_data);
|
WebPDemuxer* demux = WebPDemux(&webp_data);
|
||||||
@@ -170,12 +183,36 @@ and EXIF/XMP metadata.
|
|||||||
For a detailed Demux API reference, please refer to the header file
|
For a detailed Demux API reference, please refer to the header file
|
||||||
(src/webp/demux.h).
|
(src/webp/demux.h).
|
||||||
|
|
||||||
|
AnimEncoder API:
|
||||||
|
================
|
||||||
|
The AnimEncoder API can be used to create 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, duration, &config);
|
||||||
|
}
|
||||||
|
WebPAnimEncoderAssemble(enc, webp_data);
|
||||||
|
WebPAnimEncoderDelete(enc);
|
||||||
|
// ... (Write the 'webp_data' to a file, or re-mux it further).
|
||||||
|
|
||||||
|
|
||||||
|
For a detailed AnimEncoder API reference, please refer to the header file
|
||||||
|
(src/webp/mux.h).
|
||||||
|
|
||||||
|
|
||||||
Bugs:
|
Bugs:
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting-patches/
|
||||||
|
|
||||||
|
|||||||
404
build.gradle
Normal file
404
build.gradle
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
// Define dependencies.
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url "https://jcenter.bintray.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define versions in the project.
|
||||||
|
project.ext {
|
||||||
|
buildToolsVersion = "${BUILD_TOOLS_VERSION}"
|
||||||
|
compileSdkVersion = COMPILE_SDK_VERSION.toInteger()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core libraries and executables.
|
||||||
|
apply plugin: "c"
|
||||||
|
def NEON
|
||||||
|
model {
|
||||||
|
buildTypes {
|
||||||
|
debug
|
||||||
|
release
|
||||||
|
}
|
||||||
|
platforms {
|
||||||
|
arm {
|
||||||
|
architecture "arm"
|
||||||
|
}
|
||||||
|
arm64 {
|
||||||
|
architecture "arm64"
|
||||||
|
}
|
||||||
|
x86 {
|
||||||
|
architecture "x86"
|
||||||
|
}
|
||||||
|
x64 {
|
||||||
|
architecture "x86_64"
|
||||||
|
}
|
||||||
|
mips32r2
|
||||||
|
mips32r5
|
||||||
|
mips64r6
|
||||||
|
}
|
||||||
|
toolChains {
|
||||||
|
gcc(Gcc) {
|
||||||
|
target("mips32r2") {
|
||||||
|
cCompiler.args "-mips32r2"
|
||||||
|
}
|
||||||
|
target("mips32r5") {
|
||||||
|
cCompiler.args "-mips32r5"
|
||||||
|
}
|
||||||
|
target("mips64r6") {
|
||||||
|
cCompiler.args "-mips64r6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
if (toolChain in Gcc) {
|
||||||
|
cCompiler.args "-fPIC"
|
||||||
|
cCompiler.args "-Wall"
|
||||||
|
cCompiler.define "ANDROID"
|
||||||
|
cCompiler.define "HAVE_MALLOC_H"
|
||||||
|
}
|
||||||
|
// Optimizations.
|
||||||
|
if (buildType == buildTypes.release) {
|
||||||
|
if (toolChain in Gcc) {
|
||||||
|
cCompiler.args "-finline-functions"
|
||||||
|
cCompiler.args "-ffast-math"
|
||||||
|
cCompiler.args "-ffunction-sections"
|
||||||
|
cCompiler.args "-fdata-sections"
|
||||||
|
}
|
||||||
|
if (toolChain in Clang) {
|
||||||
|
cCompiler.args "-frename-registers -s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for NEON usage.
|
||||||
|
if (getTargetPlatform() == "arm" || getTargetPlatform() == "arm64") {
|
||||||
|
NEON = "c.neon"
|
||||||
|
} else {
|
||||||
|
NEON = "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Link to pthread for shared libraries.
|
||||||
|
withType(SharedLibraryBinarySpec) {
|
||||||
|
if (toolChain in Gcc) {
|
||||||
|
cCompiler.define "HAVE_PTHREAD"
|
||||||
|
cCompiler.define "WEBP_USE_THREAD"
|
||||||
|
linker.args "-pthread"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
components {
|
||||||
|
webp(NativeLibrarySpec) {
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "src/dec"
|
||||||
|
include "alpha_dec.c"
|
||||||
|
include "buffer_dec.c"
|
||||||
|
include "frame_dec.c"
|
||||||
|
include "idec_dec.c"
|
||||||
|
include "io_dec.c"
|
||||||
|
include "quant_dec.c"
|
||||||
|
include "tree_dec.c"
|
||||||
|
include "vp8_dec.c"
|
||||||
|
include "vp8l_dec.c"
|
||||||
|
include "webp_dec.c"
|
||||||
|
srcDir "src/dsp"
|
||||||
|
include "alpha_processing.c"
|
||||||
|
include "alpha_processing_mips_dsp_r2.c"
|
||||||
|
include "alpha_processing_neon.$NEON"
|
||||||
|
include "alpha_processing_sse2.c"
|
||||||
|
include "alpha_processing_sse41.c"
|
||||||
|
include "argb.c"
|
||||||
|
include "argb_mips_dsp_r2.c"
|
||||||
|
include "argb_sse2.c"
|
||||||
|
include "cpu.c"
|
||||||
|
include "dec.c"
|
||||||
|
include "dec_clip_tables.c"
|
||||||
|
include "dec_mips32.c"
|
||||||
|
include "dec_mips_dsp_r2.c"
|
||||||
|
include "dec_msa.c"
|
||||||
|
include "dec_neon.$NEON"
|
||||||
|
include "dec_sse2.c"
|
||||||
|
include "dec_sse41.c"
|
||||||
|
include "filters.c"
|
||||||
|
include "filters_mips_dsp_r2.c"
|
||||||
|
include "filters_msa.c"
|
||||||
|
include "filters_neon.$NEON"
|
||||||
|
include "filters_sse2.c"
|
||||||
|
include "lossless.c"
|
||||||
|
include "lossless_mips_dsp_r2.c"
|
||||||
|
include "lossless_msa.c"
|
||||||
|
include "lossless_neon.$NEON"
|
||||||
|
include "lossless_sse2.c"
|
||||||
|
include "rescaler.c"
|
||||||
|
include "rescaler_mips32.c"
|
||||||
|
include "rescaler_mips_dsp_r2.c"
|
||||||
|
include "rescaler_msa.c"
|
||||||
|
include "rescaler_neon.$NEON"
|
||||||
|
include "rescaler_sse2.c"
|
||||||
|
include "upsampling.c"
|
||||||
|
include "upsampling_mips_dsp_r2.c"
|
||||||
|
include "upsampling_msa.c"
|
||||||
|
include "upsampling_neon.$NEON"
|
||||||
|
include "upsampling_sse2.c"
|
||||||
|
include "yuv.c"
|
||||||
|
include "yuv_mips32.c"
|
||||||
|
include "yuv_mips_dsp_r2.c"
|
||||||
|
include "yuv_sse2.c"
|
||||||
|
srcDir "src/utils"
|
||||||
|
include "bit_reader_utils.c"
|
||||||
|
include "color_cache_utils.c"
|
||||||
|
include "filters_utils.c"
|
||||||
|
include "huffman_utils.c"
|
||||||
|
include "quant_levels_dec_utils.c"
|
||||||
|
include "random_utils.c"
|
||||||
|
include "rescaler_utils.c"
|
||||||
|
include "thread_utils.c"
|
||||||
|
include "utils.c"
|
||||||
|
srcDir "src/dsp"
|
||||||
|
include "cost.c"
|
||||||
|
include "cost_mips32.c"
|
||||||
|
include "cost_mips_dsp_r2.c"
|
||||||
|
include "cost_sse2.c"
|
||||||
|
include "enc.c"
|
||||||
|
include "enc_avx2.c"
|
||||||
|
include "enc_mips32.c"
|
||||||
|
include "enc_mips_dsp_r2.c"
|
||||||
|
include "enc_msa.c"
|
||||||
|
include "enc_neon.$NEON"
|
||||||
|
include "enc_sse2.c"
|
||||||
|
include "enc_sse41.c"
|
||||||
|
include "lossless_enc.c"
|
||||||
|
include "lossless_enc_mips32.c"
|
||||||
|
include "lossless_enc_mips_dsp_r2.c"
|
||||||
|
include "lossless_enc_msa.c"
|
||||||
|
include "lossless_enc_neon.$NEON"
|
||||||
|
include "lossless_enc_sse2.c"
|
||||||
|
include "lossless_enc_sse41.c"
|
||||||
|
srcDir "src/enc"
|
||||||
|
include "alpha_enc.c"
|
||||||
|
include "analysis_enc.c"
|
||||||
|
include "backward_references_enc.c"
|
||||||
|
include "config_enc.c"
|
||||||
|
include "cost_enc.c"
|
||||||
|
include "delta_palettization_enc.c"
|
||||||
|
include "filter_enc.c"
|
||||||
|
include "frame_enc.c"
|
||||||
|
include "histogram_enc.c"
|
||||||
|
include "iterator_enc.c"
|
||||||
|
include "near_lossless_enc.c"
|
||||||
|
include "picture_enc.c"
|
||||||
|
include "picture_csp_enc.c"
|
||||||
|
include "picture_psnr_enc.c"
|
||||||
|
include "picture_rescale_enc.c"
|
||||||
|
include "picture_tools_enc.c"
|
||||||
|
include "predictor_enc.c"
|
||||||
|
include "quant_enc.c"
|
||||||
|
include "syntax_enc.c"
|
||||||
|
include "token_enc.c"
|
||||||
|
include "tree_enc.c"
|
||||||
|
include "vp8l_enc.c"
|
||||||
|
include "webp_enc.c"
|
||||||
|
srcDir "src/utils"
|
||||||
|
include "bit_writer_utils.c"
|
||||||
|
include "huffman_encode_utils.c"
|
||||||
|
include "quant_levels_utils.c"
|
||||||
|
}
|
||||||
|
exportedHeaders {
|
||||||
|
srcDir "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webpdemux(NativeLibrarySpec) {
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "src/demux"
|
||||||
|
include "anim_decode.c"
|
||||||
|
include "demux.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webpmux(NativeLibrarySpec) {
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "src/mux/"
|
||||||
|
include "anim_encode.c"
|
||||||
|
include "muxedit.c"
|
||||||
|
include "muxinternal.c"
|
||||||
|
include "muxread.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executables from examples.
|
||||||
|
example_util(NativeLibrarySpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "webp", linkage: "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./examples"
|
||||||
|
include "example_util.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageio_util(NativeLibrarySpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "webp", linkage: "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./imageio"
|
||||||
|
include "imageio_util.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imagedec(NativeLibrarySpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "webp", linkage: "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./imageio"
|
||||||
|
include "image_dec.c"
|
||||||
|
include "jpegdec.c"
|
||||||
|
include "metadata.c"
|
||||||
|
include "pngdec.c"
|
||||||
|
include "tiffdec.c"
|
||||||
|
include "webpdec.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageenc(NativeLibrarySpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "webp", linkage: "static"
|
||||||
|
lib library: "imageio_util", linkage: "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./imageio"
|
||||||
|
include "image_enc.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cwebp(NativeExecutableSpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "example_util", linkage: "static"
|
||||||
|
lib library: "imagedec", linkage: "static"
|
||||||
|
lib library: "imageio_util", linkage: "static"
|
||||||
|
lib library: "webp", linkage: "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./examples"
|
||||||
|
include "cwebp.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dwebp(NativeExecutableSpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "example_util", linkage: "static"
|
||||||
|
lib library: "imagedec", linkage: "static"
|
||||||
|
lib library: "imageenc", linkage: "static"
|
||||||
|
lib library: "imageio_util", linkage: "static"
|
||||||
|
lib library: "webp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./examples"
|
||||||
|
include "dwebp.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webpmux_example(NativeExecutableSpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "example_util", linkage: "static"
|
||||||
|
lib library: "imageio_util", linkage: "static"
|
||||||
|
lib library: "webpmux", linkage: "static"
|
||||||
|
lib library: "webp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./examples"
|
||||||
|
include "webpmux.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img2webp_example(NativeExecutableSpec) {
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
lib library: "example_util", linkage: "static"
|
||||||
|
lib library: "imagedec", linkage: "static"
|
||||||
|
lib library: "imageio_util", linkage: "static"
|
||||||
|
lib library: "webpmux", linkage: "static"
|
||||||
|
lib library: "webp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "./examples"
|
||||||
|
include "img2webp.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks {
|
||||||
|
// Task to test all possible configurations.
|
||||||
|
buildAllExecutables(Task) {
|
||||||
|
dependsOn $.binaries.findAll { it.buildable }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task to generate the wrapper.
|
||||||
|
task wrapper(type: Wrapper) {
|
||||||
|
gradleVersion = '2.13'
|
||||||
|
}
|
||||||
141
cmake/config.h.cmake
Normal file
141
cmake/config.h.cmake
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# Generate the config.h to compile with specific intrinsics / libs.
|
||||||
|
|
||||||
|
## Check for compiler options.
|
||||||
|
include(CheckCSourceCompiles)
|
||||||
|
check_c_source_compiles("
|
||||||
|
int main(void) {
|
||||||
|
(void)__builtin_bswap16(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
HAVE_BUILTIN_BSWAP16
|
||||||
|
)
|
||||||
|
check_c_source_compiles("
|
||||||
|
int main(void) {
|
||||||
|
(void)__builtin_bswap32(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
HAVE_BUILTIN_BSWAP32
|
||||||
|
)
|
||||||
|
check_c_source_compiles("
|
||||||
|
int main(void) {
|
||||||
|
(void)__builtin_bswap64(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
HAVE_BUILTIN_BSWAP64
|
||||||
|
)
|
||||||
|
|
||||||
|
## Check for libraries.
|
||||||
|
find_package(Threads)
|
||||||
|
if(Threads_FOUND)
|
||||||
|
if(CMAKE_USE_PTHREADS_INIT)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||||
|
endif()
|
||||||
|
foreach(PTHREAD_TEST HAVE_PTHREAD_PRIO_INHERIT PTHREAD_CREATE_UNDETACHED)
|
||||||
|
check_c_source_compiles("
|
||||||
|
#include <pthread.h>
|
||||||
|
int main (void) {
|
||||||
|
int attr = ${PTHREAD_TEST};
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
" ${PTHREAD_TEST}
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
list(APPEND WEBP_DEP_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
endif()
|
||||||
|
set(WEBP_USE_THREAD ${Threads_FOUND})
|
||||||
|
|
||||||
|
# TODO: this seems unused, check with autotools.
|
||||||
|
set(LT_OBJDIR ".libs/")
|
||||||
|
|
||||||
|
# Only useful for vwebp, so useless for now.
|
||||||
|
# find_package(OpenGL)
|
||||||
|
# set(WEBP_HAVE_GL ${OPENGL_FOUND})
|
||||||
|
# set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS})
|
||||||
|
# set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} ${OPENGL_LIBRARIES})
|
||||||
|
|
||||||
|
# Find the standard C math library.
|
||||||
|
find_library(MATH_LIBRARY NAMES m)
|
||||||
|
if(MATH_LIBRARY)
|
||||||
|
list(APPEND WEBP_DEP_LIBRARIES ${MATH_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find the standard image libraries.
|
||||||
|
set(WEBP_DEP_IMG_LIBRARIES)
|
||||||
|
set(WEBP_DEP_IMG_INCLUDE_DIRS)
|
||||||
|
foreach(I_LIB PNG JPEG TIFF GIF)
|
||||||
|
find_package(${I_LIB})
|
||||||
|
set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
|
||||||
|
if(${I_LIB}_FOUND)
|
||||||
|
list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
|
||||||
|
list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
## Check for specific headers.
|
||||||
|
include(CheckIncludeFiles)
|
||||||
|
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
|
||||||
|
check_include_files(dlfcn.h HAVE_DLFCN_H)
|
||||||
|
check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
|
||||||
|
check_include_files(GL/glut.h HAVE_GL_GLUT_H)
|
||||||
|
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||||
|
check_include_files(memory.h HAVE_MEMORY_H)
|
||||||
|
check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
|
||||||
|
check_include_files(shlwapi.h HAVE_SHLWAPI_H)
|
||||||
|
check_include_files(stdint.h HAVE_STDINT_H)
|
||||||
|
check_include_files(stdlib.h HAVE_STDLIB_H)
|
||||||
|
check_include_files(strings.h HAVE_STRINGS_H)
|
||||||
|
check_include_files(string.h HAVE_STRING_H)
|
||||||
|
check_include_files(sys/stat.h HAVE_SYS_STAT_H)
|
||||||
|
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||||
|
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||||
|
check_include_files(wincodec.h HAVE_WINCODEC_H)
|
||||||
|
check_include_files(windows.h HAVE_WINDOWS_H)
|
||||||
|
|
||||||
|
# Windows specifics
|
||||||
|
if(HAVE_WINCODEC_H)
|
||||||
|
list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Check for SIMD extensions.
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake)
|
||||||
|
|
||||||
|
## Define extra info.
|
||||||
|
set(PACKAGE ${PROJECT_NAME})
|
||||||
|
set(PACKAGE_NAME ${PROJECT_NAME})
|
||||||
|
|
||||||
|
# Read from configure.ac.
|
||||||
|
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
|
||||||
|
string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]"
|
||||||
|
CONFIGURE_AC_PACKAGE_INFO ${CONFIGURE_AC}
|
||||||
|
)
|
||||||
|
function(strip_bracket VAR)
|
||||||
|
string(LENGTH ${${VAR}} TMP_LEN)
|
||||||
|
math(EXPR TMP_LEN ${TMP_LEN}-2)
|
||||||
|
string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
|
||||||
|
set(${VAR} ${TMP_SUB} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION)
|
||||||
|
strip_bracket(PACKAGE_VERSION)
|
||||||
|
list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT)
|
||||||
|
strip_bracket(PACKAGE_BUGREPORT)
|
||||||
|
list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL)
|
||||||
|
strip_bracket(PACKAGE_URL)
|
||||||
|
|
||||||
|
# Build more info.
|
||||||
|
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||||
|
set(PACKAGE_TARNAME ${PACKAGE_NAME})
|
||||||
|
set(VERSION ${PACKAGE_VERSION})
|
||||||
|
|
||||||
|
## Generate the config.h header.
|
||||||
|
configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include/webp/config.h)
|
||||||
|
add_definitions(-DHAVE_CONFIG_H)
|
||||||
|
# The webp folder is included as we reference config.h as
|
||||||
|
# ../webp/config.h or webp/config.h
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include/webp
|
||||||
|
)
|
||||||
143
cmake/config.h.in
Normal file
143
cmake/config.h.in
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/* Adapted from the autotools src/webp/config.h.in. */
|
||||||
|
|
||||||
|
/* Define if building universal (internal helper macro) */
|
||||||
|
/* TODO: handle properly in CMake */
|
||||||
|
#cmakedefine AC_APPLE_UNIVERSAL_BUILD 1
|
||||||
|
|
||||||
|
/* Set to 1 if __builtin_bswap16 is available */
|
||||||
|
#cmakedefine HAVE_BUILTIN_BSWAP16 1
|
||||||
|
|
||||||
|
/* Set to 1 if __builtin_bswap32 is available */
|
||||||
|
#cmakedefine HAVE_BUILTIN_BSWAP32 1
|
||||||
|
|
||||||
|
/* Set to 1 if __builtin_bswap64 is available */
|
||||||
|
#cmakedefine HAVE_BUILTIN_BSWAP64 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#cmakedefine HAVE_DLFCN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <GLUT/glut.h> header file. */
|
||||||
|
#cmakedefine HAVE_GLUT_GLUT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <GL/glut.h> header file. */
|
||||||
|
#cmakedefine HAVE_GL_GLUT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#cmakedefine HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#cmakedefine HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <OpenGL/glut.h> header file. */
|
||||||
|
#cmakedefine HAVE_OPENGL_GLUT_H 1
|
||||||
|
|
||||||
|
/* Have PTHREAD_PRIO_INHERIT. */
|
||||||
|
#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <shlwapi.h> header file. */
|
||||||
|
#cmakedefine HAVE_SHLWAPI_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#cmakedefine HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#cmakedefine HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#cmakedefine HAVE_STRINGS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#cmakedefine HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#cmakedefine HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#cmakedefine HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#cmakedefine HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <wincodec.h> header file. */
|
||||||
|
#cmakedefine HAVE_WINCODEC_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <windows.h> header file. */
|
||||||
|
#cmakedefine HAVE_WINDOWS_H 1
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
/* TODO: handle properly in CMake */
|
||||||
|
#cmakedefine LT_OBJDIR "@LT_OBJDIR@"
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#cmakedefine PACKAGE "@PROJECT_NAME@"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#cmakedefine PACKAGE_URL "@PACKAGE_URL@"
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
|
||||||
|
|
||||||
|
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||||
|
your system. */
|
||||||
|
#cmakedefine PTHREAD_CREATE_JOINABLE 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#cmakedefine STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#cmakedefine VERSION "@VERSION@"
|
||||||
|
|
||||||
|
/* Enable experimental code */
|
||||||
|
#cmakedefine WEBP_EXPERIMENTAL_FEATURES 1
|
||||||
|
|
||||||
|
/* Set to 1 if AVX2 is supported */
|
||||||
|
#cmakedefine WEBP_HAVE_AVX2 1
|
||||||
|
|
||||||
|
/* Set to 1 if GIF library is installed */
|
||||||
|
#cmakedefine WEBP_HAVE_GIF 1
|
||||||
|
|
||||||
|
/* Set to 1 if OpenGL is supported */
|
||||||
|
#cmakedefine WEBP_HAVE_GL 1
|
||||||
|
|
||||||
|
/* Set to 1 if JPEG library is installed */
|
||||||
|
#cmakedefine WEBP_HAVE_JPEG 1
|
||||||
|
|
||||||
|
/* Set to 1 if PNG library is installed */
|
||||||
|
#cmakedefine WEBP_HAVE_PNG 1
|
||||||
|
|
||||||
|
/* Set to 1 if SSE2 is supported */
|
||||||
|
#cmakedefine WEBP_HAVE_SSE2 1
|
||||||
|
|
||||||
|
/* Set to 1 if SSE4.1 is supported */
|
||||||
|
#cmakedefine WEBP_HAVE_SSE41 1
|
||||||
|
|
||||||
|
/* Set to 1 if TIFF library is installed */
|
||||||
|
#cmakedefine WEBP_HAVE_TIFF 1
|
||||||
|
|
||||||
|
/* Undefine this to disable thread support. */
|
||||||
|
#cmakedefine 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
|
||||||
113
cmake/cpu.cmake
Normal file
113
cmake/cpu.cmake
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
## Check for SIMD extensions.
|
||||||
|
|
||||||
|
function(webp_check_compiler_flag WEBP_SIMD_FLAG)
|
||||||
|
unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
|
||||||
|
check_c_source_compiles("
|
||||||
|
#include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
|
||||||
|
int main(void) {
|
||||||
|
#if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
|
||||||
|
this is not valid code
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
" WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG}
|
||||||
|
)
|
||||||
|
if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
|
||||||
|
set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# those are included in the names of WEBP_USE_* in c++ code.
|
||||||
|
set(WEBP_SIMD_FLAGS "SSE2;SSE41;AVX2;MIPS32;MIPS_DSP_R2;NEON;MSA")
|
||||||
|
set(WEBP_SIMD_FILE_EXTENSIONS "_sse2.c;_sse41.c;_avx2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
|
||||||
|
if(MSVC)
|
||||||
|
# MSVC does not have a SSE4 flag but AVX2 support implies
|
||||||
|
# SSE4 support.
|
||||||
|
set(SIMD_ENABLE_FLAGS "/arch:SSE2;/arch:AVX2;/arch:AVX2;;;;")
|
||||||
|
set(SIMD_DISABLE_FLAGS)
|
||||||
|
else()
|
||||||
|
set(SIMD_ENABLE_FLAGS "-msse2;-msse4.1;-mavx2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
|
||||||
|
set(SIMD_DISABLE_FLAGS "-mno-sse2;-mno-sse4.1;-mno-avx2;;-mno-dspr2;;-mno-msa")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(WEBP_SIMD_FILES_TO_NOT_INCLUDE)
|
||||||
|
set(WEBP_SIMD_FILES_TO_INCLUDE)
|
||||||
|
set(WEBP_SIMD_FLAGS_TO_INCLUDE)
|
||||||
|
|
||||||
|
if(${ANDROID})
|
||||||
|
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
|
||||||
|
# This is because Android studio uses the configuration
|
||||||
|
# "-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16"
|
||||||
|
# that does not trigger neon optimizations but should
|
||||||
|
# (as this configuration does not exist anymore).
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
|
||||||
|
math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
|
||||||
|
|
||||||
|
foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
|
||||||
|
list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
|
||||||
|
|
||||||
|
# First try with no extra flag added as the compiler might have default flags
|
||||||
|
# (especially on Android).
|
||||||
|
unset(WEBP_HAVE_${WEBP_SIMD_FLAG} CACHE)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS)
|
||||||
|
webp_check_compiler_flag(${WEBP_SIMD_FLAG})
|
||||||
|
if(NOT WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||||
|
list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
|
||||||
|
webp_check_compiler_flag(${WEBP_SIMD_FLAG})
|
||||||
|
else()
|
||||||
|
set(SIMD_COMPILE_FLAG " ")
|
||||||
|
endif()
|
||||||
|
# Check which files we should include or not.
|
||||||
|
list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
|
||||||
|
file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
|
||||||
|
"src/dsp/*${WEBP_SIMD_FILE_EXTENSION}"
|
||||||
|
)
|
||||||
|
if(WEBP_HAVE_${WEBP_SIMD_FLAG})
|
||||||
|
# Memorize the file and flags.
|
||||||
|
foreach(FILE ${SIMD_FILES})
|
||||||
|
list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE})
|
||||||
|
list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG})
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
# Remove the file from the list.
|
||||||
|
foreach(FILE ${SIMD_FILES})
|
||||||
|
list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE})
|
||||||
|
endforeach()
|
||||||
|
# Explicitly disable SIMD.
|
||||||
|
if(SIMD_DISABLE_FLAGS)
|
||||||
|
list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
if(SIMD_COMPILE_FLAG)
|
||||||
|
unset(HAS_COMPILE_FLAG CACHE)
|
||||||
|
check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
|
||||||
|
if(HAS_COMPILE_FLAG)
|
||||||
|
# Do one more check for Clang to circumvent CMake issue 13194.
|
||||||
|
if(COMMAND check_compiler_flag_common_patterns)
|
||||||
|
# Only in CMake 3.0 and above.
|
||||||
|
check_compiler_flag_common_patterns(COMMON_PATTERNS)
|
||||||
|
else()
|
||||||
|
set(COMMON_PATTERNS)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG})
|
||||||
|
check_c_source_compiles("int main(void) {return 0;}" FLAG2
|
||||||
|
FAIL_REGEX "warning: argument unused during compilation:"
|
||||||
|
${COMMON_PATTERNS}
|
||||||
|
)
|
||||||
|
if(NOT FLAG2)
|
||||||
|
unset(HAS_COMPILE_FLAG CACHE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(HAS_COMPILE_FLAG)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
230
configure.ac
230
configure.ac
@@ -1,7 +1,7 @@
|
|||||||
AC_INIT([libwebp], [0.4.1],
|
AC_INIT([libwebp], [0.6.0],
|
||||||
[http://code.google.com/p/webp/issues],,
|
[https://bugs.chromium.org/p/webp],,
|
||||||
[http://developers.google.com/speed/webp])
|
[http://developers.google.com/speed/webp])
|
||||||
AC_CANONICAL_TARGET
|
AC_CANONICAL_HOST
|
||||||
AC_PREREQ([2.60])
|
AC_PREREQ([2.60])
|
||||||
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
|
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
|
|||||||
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
|
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
|
||||||
|
|
||||||
AC_PROG_LIBTOOL
|
AC_PROG_LIBTOOL
|
||||||
|
AC_PROG_SED
|
||||||
AM_PROG_CC_C_O
|
AM_PROG_CC_C_O
|
||||||
|
|
||||||
dnl === Enable less verbose output when building.
|
dnl === Enable less verbose output when building.
|
||||||
@@ -28,8 +29,21 @@ AC_ARG_ENABLE([everything],
|
|||||||
disabled with --disable-target]),
|
disabled with --disable-target]),
|
||||||
[SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
|
[SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
|
||||||
SET_IF_UNSET([enable_libwebpdemux], [$enableval])
|
SET_IF_UNSET([enable_libwebpdemux], [$enableval])
|
||||||
|
SET_IF_UNSET([enable_libwebpextras], [$enableval])
|
||||||
SET_IF_UNSET([enable_libwebpmux], [$enableval])])
|
SET_IF_UNSET([enable_libwebpmux], [$enableval])])
|
||||||
|
|
||||||
|
dnl === If --enable-asserts is not defined, define NDEBUG
|
||||||
|
|
||||||
|
AC_MSG_CHECKING(whether asserts are enabled)
|
||||||
|
AC_ARG_ENABLE([asserts],
|
||||||
|
AS_HELP_STRING([--enable-asserts],
|
||||||
|
[Enable assert checks]))
|
||||||
|
if test "x${enable_asserts-no}" = "xno"; then
|
||||||
|
AM_CPPFLAGS="${AM_CPPFLAGS} -DNDEBUG"
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT(${enable_asserts-no})
|
||||||
|
AC_SUBST([AM_CPPFLAGS])
|
||||||
|
|
||||||
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
|
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
|
||||||
[Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
|
[Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
|
||||||
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
|
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
|
||||||
@@ -51,22 +65,70 @@ AC_DEFUN([TEST_AND_ADD_CFLAGS],
|
|||||||
[$1="${$1} $2"],
|
[$1="${$1} $2"],
|
||||||
[AC_MSG_RESULT([no])])
|
[AC_MSG_RESULT([no])])
|
||||||
CFLAGS="$SAVED_CFLAGS"])
|
CFLAGS="$SAVED_CFLAGS"])
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-fvisibility=hidden])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat-security])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion])
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral])
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
|
||||||
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla])
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla])
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622
|
||||||
|
AS_IF([test "$GCC" = "yes" ], [
|
||||||
|
gcc_version=`$CC -dumpversion`
|
||||||
|
gcc_wht_bug=""
|
||||||
|
case "$host_cpu" in
|
||||||
|
aarch64|arm64)
|
||||||
|
case "$gcc_version" in
|
||||||
|
4.9|4.9.0|4.9.1) gcc_wht_bug=yes ;;
|
||||||
|
esac
|
||||||
|
esac
|
||||||
|
AS_IF([test "$gcc_wht_bug" = "yes"], [
|
||||||
|
TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])])
|
||||||
|
# Use -flax-vector-conversions, if available, when building intrinsics with
|
||||||
|
# older versions of gcc. The flag appeared in 4.3.x, but if backported, and
|
||||||
|
# -fno-lax-vector-conversions is set, errors may occur with the intrinsics
|
||||||
|
# files along with the older system includes, e.g., emmintrin.h.
|
||||||
|
# Originally observed with cc (GCC) 4.2.1 20070831 patched [FreeBSD] (9.3).
|
||||||
|
# https://bugs.chromium.org/p/webp/issues/detail?id=274
|
||||||
|
AS_IF([test "$GCC" = "yes" ], [
|
||||||
|
case "$host_cpu" in
|
||||||
|
amd64|i?86|x86_64)
|
||||||
|
AC_COMPILE_IFELSE(
|
||||||
|
dnl only check for -flax-vector-conversions with older gcc, skip
|
||||||
|
dnl clang as it reports itself as 4.2.1, but the flag isn't needed.
|
||||||
|
[AC_LANG_SOURCE([#if !defined(__clang__) && defined(__GNUC__) && \
|
||||||
|
((__GNUC__ << 8) | __GNUC_MINOR__) < 0x403
|
||||||
|
#error old gcc
|
||||||
|
#endif
|
||||||
|
int main(void) { return 0; }
|
||||||
|
])],,
|
||||||
|
[TEST_AND_ADD_CFLAGS([INTRINSICS_CFLAGS],
|
||||||
|
[-flax-vector-conversions])])
|
||||||
|
;;
|
||||||
|
esac])
|
||||||
AC_SUBST([AM_CFLAGS])
|
AC_SUBST([AM_CFLAGS])
|
||||||
|
|
||||||
dnl === Check for machine specific flags
|
dnl === Check for machine specific flags
|
||||||
TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
|
AC_ARG_ENABLE([avx2],
|
||||||
AS_IF([test -n "$AVX2_FLAGS"], [
|
AS_HELP_STRING([--disable-avx2],
|
||||||
|
[Disable detection of AVX2 support
|
||||||
|
@<:@default=auto@:>@]))
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_avx2" != "xno" -a "x$enable_sse4_1" != "xno" \
|
||||||
|
-a "x$enable_sse2" != "xno"], [
|
||||||
|
AVX2_CFLAGS="$INTRINSICS_CFLAGS $AVX2_FLAGS"
|
||||||
|
TEST_AND_ADD_CFLAGS([AVX2_FLAGS], [-mavx2])
|
||||||
|
AS_IF([test -n "$AVX2_FLAGS"], [
|
||||||
SAVED_CFLAGS=$CFLAGS
|
SAVED_CFLAGS=$CFLAGS
|
||||||
CFLAGS="$CFLAGS $AVX2_FLAGS"
|
CFLAGS="$CFLAGS $AVX2_FLAGS"
|
||||||
AC_CHECK_HEADER([immintrin.h],
|
AC_CHECK_HEADER([immintrin.h],
|
||||||
@@ -80,10 +142,35 @@ AS_IF([test -n "$AVX2_FLAGS"], [
|
|||||||
#endif
|
#endif
|
||||||
])
|
])
|
||||||
CFLAGS=$SAVED_CFLAGS])
|
CFLAGS=$SAVED_CFLAGS])
|
||||||
AC_SUBST([AVX2_FLAGS])
|
AC_SUBST([AVX2_FLAGS])])
|
||||||
|
|
||||||
TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2])
|
AC_ARG_ENABLE([sse4.1],
|
||||||
AS_IF([test -n "$SSE2_FLAGS"], [
|
AS_HELP_STRING([--disable-sse4.1],
|
||||||
|
[Disable detection of SSE4.1 support
|
||||||
|
@<:@default=auto@:>@]))
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [
|
||||||
|
SSE41_FLAGS="$INTRINSICS_CFLAGS $SSE41_FLAGS"
|
||||||
|
TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1])
|
||||||
|
AS_IF([test -n "$SSE41_FLAGS"], [
|
||||||
|
SAVED_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$CFLAGS $SSE41_FLAGS"
|
||||||
|
AC_CHECK_HEADER([smmintrin.h],
|
||||||
|
[AC_DEFINE(WEBP_HAVE_SSE41, [1],
|
||||||
|
[Set to 1 if SSE4.1 is supported])],
|
||||||
|
[SSE41_FLAGS=""])
|
||||||
|
CFLAGS=$SAVED_CFLAGS])
|
||||||
|
AC_SUBST([SSE41_FLAGS])])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([sse2],
|
||||||
|
AS_HELP_STRING([--disable-sse2],
|
||||||
|
[Disable detection of SSE2 support
|
||||||
|
@<:@default=auto@:>@]))
|
||||||
|
|
||||||
|
AS_IF([test "x$enable_sse2" != "xno"], [
|
||||||
|
SSE2_FLAGS="$INTRINSICS_CFLAGS $SSE2_FLAGS"
|
||||||
|
TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2])
|
||||||
|
AS_IF([test -n "$SSE2_FLAGS"], [
|
||||||
SAVED_CFLAGS=$CFLAGS
|
SAVED_CFLAGS=$CFLAGS
|
||||||
CFLAGS="$CFLAGS $SSE2_FLAGS"
|
CFLAGS="$CFLAGS $SSE2_FLAGS"
|
||||||
AC_CHECK_HEADER([emmintrin.h],
|
AC_CHECK_HEADER([emmintrin.h],
|
||||||
@@ -91,7 +178,72 @@ AS_IF([test -n "$SSE2_FLAGS"], [
|
|||||||
[Set to 1 if SSE2 is supported])],
|
[Set to 1 if SSE2 is supported])],
|
||||||
[SSE2_FLAGS=""])
|
[SSE2_FLAGS=""])
|
||||||
CFLAGS=$SAVED_CFLAGS])
|
CFLAGS=$SAVED_CFLAGS])
|
||||||
AC_SUBST([SSE2_FLAGS])
|
AC_SUBST([SSE2_FLAGS])])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([neon],
|
||||||
|
AS_HELP_STRING([--disable-neon],
|
||||||
|
[Disable detection of NEON support
|
||||||
|
@<:@default=auto@:>@]))
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([neon_rtcd],
|
||||||
|
AS_HELP_STRING([--disable-neon-rtcd],
|
||||||
|
[Disable runtime detection of NEON support via
|
||||||
|
/proc/cpuinfo on Linux hosts
|
||||||
|
@<:@default=auto@:>@]))
|
||||||
|
# For ARM(7) hosts:
|
||||||
|
# Both NEON flags unset and NEON support detected = build all modules with NEON
|
||||||
|
# NEON detected with the use of -mfpu=neon = build only NEON modules with NEON
|
||||||
|
AS_IF([test "x$enable_neon" != "xno"], [
|
||||||
|
case "$host_cpu" in
|
||||||
|
arm|armv7*)
|
||||||
|
# Test for NEON support without flags before falling back to -mfpu=neon
|
||||||
|
for flag in '' '-mfpu=neon'; do
|
||||||
|
LOCAL_NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS"
|
||||||
|
TEST_AND_ADD_CFLAGS([LOCAL_NEON_FLAGS], [$flag])
|
||||||
|
SAVED_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$CFLAGS $LOCAL_NEON_FLAGS"
|
||||||
|
|
||||||
|
dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
|
#include <arm_neon.h>
|
||||||
|
int main(void) {
|
||||||
|
int8x8_t v = vdup_n_s8(0);
|
||||||
|
(void)v;
|
||||||
|
return 0;
|
||||||
|
}])],
|
||||||
|
[NEON_FLAGS="$(echo $LOCAL_NEON_FLAGS | $SED 's/^ *//')"
|
||||||
|
AS_IF([test -n "$NEON_FLAGS"], [
|
||||||
|
AS_IF([test "${host_os%%-*}" = "linux" -o \
|
||||||
|
"x$enable_neon_rtcd" = "xno"], [
|
||||||
|
CFLAGS=$SAVED_CFLAGS
|
||||||
|
AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
|
||||||
|
break
|
||||||
|
],[
|
||||||
|
AC_MSG_WARN(m4_normalize([NEON runtime cpu-detection is
|
||||||
|
unavailable for ${host_os%%-*}. Force
|
||||||
|
with CFLAGS=-mfpu=neon or
|
||||||
|
--disable-neon-rtcd.]))
|
||||||
|
enable_neon_rtcd=no
|
||||||
|
NEON_FLAGS=""
|
||||||
|
])
|
||||||
|
],[
|
||||||
|
CFLAGS=$SAVED_CFLAGS
|
||||||
|
AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
|
||||||
|
break
|
||||||
|
])])
|
||||||
|
CFLAGS=$SAVED_CFLAGS
|
||||||
|
done
|
||||||
|
|
||||||
|
AS_IF([test -n "$NEON_FLAGS"], [
|
||||||
|
# If NEON is available and rtcd is disabled apply NEON_FLAGS globally.
|
||||||
|
AS_IF([test "x$enable_neon_rtcd" = "xno"], [
|
||||||
|
AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS"
|
||||||
|
NEON_FLAGS=""],
|
||||||
|
[AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1],
|
||||||
|
[Set to 1 if runtime detection of NEON is enabled])])])
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
AC_SUBST([NEON_FLAGS])])
|
||||||
|
|
||||||
dnl === CLEAR_LIBVARS([var_pfx])
|
dnl === CLEAR_LIBVARS([var_pfx])
|
||||||
dnl === Clears <var_pfx>_{INCLUDES,LIBS}.
|
dnl === Clears <var_pfx>_{INCLUDES,LIBS}.
|
||||||
@@ -136,7 +288,7 @@ dnl === AC_DEFINE'ing <define> if successful.
|
|||||||
AC_DEFUN([CHECK_FOR_BUILTIN],
|
AC_DEFUN([CHECK_FOR_BUILTIN],
|
||||||
[AC_LANG_PUSH([C])
|
[AC_LANG_PUSH([C])
|
||||||
AC_MSG_CHECKING([for $1])
|
AC_MSG_CHECKING([for $1])
|
||||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [$1($2)])],
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [(void)$1($2)])],
|
||||||
[AC_MSG_RESULT([yes])
|
[AC_MSG_RESULT([yes])
|
||||||
AC_DEFINE([$3], [1],
|
AC_DEFINE([$3], [1],
|
||||||
[Set to 1 if $1 is available])],
|
[Set to 1 if $1 is available])],
|
||||||
@@ -283,15 +435,12 @@ AC_ARG_ENABLE([png], AS_HELP_STRING([--disable-png],
|
|||||||
@<:@default=auto@:>@]))
|
@<:@default=auto@:>@]))
|
||||||
AS_IF([test "x$enable_png" != "xno"], [
|
AS_IF([test "x$enable_png" != "xno"], [
|
||||||
CLEAR_LIBVARS([PNG])
|
CLEAR_LIBVARS([PNG])
|
||||||
AC_PATH_PROGS(LIBPNG_CONFIG,
|
AC_PATH_PROGS([LIBPNG_CONFIG],
|
||||||
[libpng-config libpng15-config libpng14-config libpng12-config])
|
[libpng-config libpng16-config libpng15-config libpng14-config \
|
||||||
|
libpng12-config])
|
||||||
if test -n "$LIBPNG_CONFIG"; then
|
if test -n "$LIBPNG_CONFIG"; then
|
||||||
PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
|
PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
|
||||||
PNG_PREFIX=`$LIBPNG_CONFIG --prefix`
|
PNG_LIBS="`$LIBPNG_CONFIG --ldflags`"
|
||||||
if test "${PNG_PREFIX}/lib" != "/usr/lib" ; then
|
|
||||||
PNG_LIBS="-L${PNG_PREFIX}/lib"
|
|
||||||
fi
|
|
||||||
PNG_LIBS="$PNG_LIBS `$LIBPNG_CONFIG --libs`"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
WITHLIB_OPTION([png], [PNG])
|
WITHLIB_OPTION([png], [PNG])
|
||||||
@@ -394,13 +543,24 @@ AS_IF([test "x$enable_gif" != "xno"], [
|
|||||||
)
|
)
|
||||||
LIBCHECK_EPILOGUE([GIF])
|
LIBCHECK_EPILOGUE([GIF])
|
||||||
|
|
||||||
|
if test "$gif_support" = "yes" -a \
|
||||||
|
"$enable_libwebpdemux" = "yes"; then
|
||||||
|
build_animdiff=yes
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$gif_support" = "yes" -a \
|
if test "$gif_support" = "yes" -a \
|
||||||
"$enable_libwebpmux" = "yes"; then
|
"$enable_libwebpmux" = "yes"; then
|
||||||
build_gif2webp=yes
|
build_gif2webp=yes
|
||||||
fi
|
fi
|
||||||
])
|
])
|
||||||
|
AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_animdiff}" = "yes"])
|
||||||
AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
|
AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
|
||||||
|
|
||||||
|
if test "$enable_libwebpmux" = "yes"; then
|
||||||
|
build_img2webp=yes
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"])
|
||||||
|
|
||||||
dnl === check for WIC support ===
|
dnl === check for WIC support ===
|
||||||
|
|
||||||
AC_ARG_ENABLE([wic],
|
AC_ARG_ENABLE([wic],
|
||||||
@@ -409,8 +569,9 @@ AC_ARG_ENABLE([wic],
|
|||||||
@<:@default=auto@:>@]),,
|
@<:@default=auto@:>@]),,
|
||||||
[enable_wic=yes])
|
[enable_wic=yes])
|
||||||
|
|
||||||
if test \( "$target_os" = "mingw32" -o "$target_os" = "mingw64" \) \
|
case $host_os in
|
||||||
-a "$enable_wic" = "yes"; then
|
mingw*)
|
||||||
|
if test "$enable_wic" = "yes"; then
|
||||||
AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h])
|
AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h])
|
||||||
if test "$ac_cv_header_wincodec_h" = "yes"; then
|
if test "$ac_cv_header_wincodec_h" = "yes"; then
|
||||||
AC_MSG_CHECKING(for Windows Imaging Component support)
|
AC_MSG_CHECKING(for Windows Imaging Component support)
|
||||||
@@ -450,19 +611,7 @@ if test \( "$target_os" = "mingw32" -o "$target_os" = "mingw64" \) \
|
|||||||
AC_MSG_RESULT(${wic_support-no})
|
AC_MSG_RESULT(${wic_support-no})
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
esac
|
||||||
dnl === If --enable-aligned is defined, define WEBP_FORCE_ALIGNED
|
|
||||||
|
|
||||||
AC_MSG_CHECKING(if --enable-aligned option is specified)
|
|
||||||
AC_ARG_ENABLE([aligned],
|
|
||||||
AS_HELP_STRING([--enable-aligned],
|
|
||||||
[Force aligned memory operations in non-dsp code
|
|
||||||
(may be slower)]))
|
|
||||||
if test "$enable_aligned" = "yes"; then
|
|
||||||
AC_DEFINE(WEBP_FORCE_ALIGNED, [1],
|
|
||||||
[Define to 1 to force aligned memory operations])
|
|
||||||
fi
|
|
||||||
AC_MSG_RESULT(${enable_aligned-no})
|
|
||||||
|
|
||||||
dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP
|
dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP
|
||||||
|
|
||||||
@@ -514,13 +663,21 @@ AC_ARG_ENABLE([libwebpdecoder],
|
|||||||
AC_MSG_RESULT(${enable_libwebpdecoder-no})
|
AC_MSG_RESULT(${enable_libwebpdecoder-no})
|
||||||
AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
|
AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
|
||||||
|
|
||||||
|
dnl === Check whether libwebpextras should be built
|
||||||
|
AC_MSG_CHECKING(whether libwebpextras is to be built)
|
||||||
|
AC_ARG_ENABLE([libwebpextras],
|
||||||
|
AS_HELP_STRING([--enable-libwebpextras],
|
||||||
|
[Build libwebpextras @<:@default=no@:>@]))
|
||||||
|
AC_MSG_RESULT(${enable_libwebpextras-no})
|
||||||
|
AM_CONDITIONAL([WANT_EXTRAS], [test "$enable_libwebpextras" = "yes"])
|
||||||
|
|
||||||
dnl =========================
|
dnl =========================
|
||||||
|
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AC_CONFIG_HEADERS([src/webp/config.h])
|
AC_CONFIG_HEADERS([src/webp/config.h])
|
||||||
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
|
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
|
||||||
examples/Makefile src/dec/Makefile \
|
examples/Makefile extras/Makefile imageio/Makefile \
|
||||||
src/enc/Makefile src/dsp/Makefile \
|
src/dec/Makefile src/enc/Makefile src/dsp/Makefile \
|
||||||
src/demux/Makefile src/mux/Makefile \
|
src/demux/Makefile src/mux/Makefile \
|
||||||
src/utils/Makefile \
|
src/utils/Makefile \
|
||||||
src/libwebp.pc src/libwebpdecoder.pc \
|
src/libwebp.pc src/libwebpdecoder.pc \
|
||||||
@@ -540,6 +697,7 @@ libwebp: yes
|
|||||||
libwebpdecoder: ${enable_libwebpdecoder-no}
|
libwebpdecoder: ${enable_libwebpdecoder-no}
|
||||||
libwebpdemux: ${enable_libwebpdemux-no}
|
libwebpdemux: ${enable_libwebpdemux-no}
|
||||||
libwebpmux: ${enable_libwebpmux-no}
|
libwebpmux: ${enable_libwebpmux-no}
|
||||||
|
libwebpextras: ${enable_libwebpextras-no}
|
||||||
|
|
||||||
Tools:
|
Tools:
|
||||||
cwebp : yes
|
cwebp : yes
|
||||||
@@ -555,7 +713,9 @@ dwebp : yes
|
|||||||
PNG : ${png_support-no}
|
PNG : ${png_support-no}
|
||||||
WIC : ${wic_support-no}
|
WIC : ${wic_support-no}
|
||||||
GIF support : ${gif_support-no}
|
GIF support : ${gif_support-no}
|
||||||
|
anim_diff : ${build_animdiff-no}
|
||||||
gif2webp : ${build_gif2webp-no}
|
gif2webp : ${build_gif2webp-no}
|
||||||
|
img2webp : ${build_img2webp-no}
|
||||||
webpmux : ${enable_libwebpmux-no}
|
webpmux : ${enable_libwebpmux-no}
|
||||||
vwebp : ${build_vwebp-no}
|
vwebp : ${build_vwebp-no}
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -46,25 +46,16 @@ for:
|
|||||||
* **Animation.** An image may have multiple frames with pauses between them,
|
* **Animation.** An image may have multiple frames with pauses between them,
|
||||||
making it an animation.
|
making it an animation.
|
||||||
|
|
||||||
* **Image Fragmentation.** A single bitstream in WebP has an inherent
|
|
||||||
limitation for width or height of 2^14 pixels, and, when using VP8, a 512
|
|
||||||
KiB limit on the size of the first compressed partition. To support larger
|
|
||||||
images, the format supports images that are composed of multiple fragments,
|
|
||||||
each encoded as a separate bitstream. All fragments logically form a single
|
|
||||||
image: they have common metadata, color profile, etc. Image fragmentation
|
|
||||||
may also improve efficiency for larger images, e.g., grass can be encoded
|
|
||||||
differently than sky.
|
|
||||||
|
|
||||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||||
document are to be interpreted as described in [RFC 2119][].
|
document are to be interpreted as described in [RFC 2119][].
|
||||||
|
|
||||||
|
Bit numbering in chunk diagrams starts at `0` for the most significant bit
|
||||||
|
('MSB 0') as described in [RFC 1166][].
|
||||||
|
|
||||||
**Note:** Out of the features mentioned above, lossy compression, lossless
|
**Note:** Out of the features mentioned above, lossy compression, lossless
|
||||||
compression, transparency, metadata, color profile and animation are finalized
|
compression, transparency, metadata, color profile and animation are finalized
|
||||||
and are to be considered stable. On the other hand, image fragmentation is
|
and are to be considered stable.
|
||||||
experimental as of now, and is open to discussion, feedback and comments.
|
|
||||||
The same is indicated using annotation "_status: experimental_" in the relevant
|
|
||||||
sections of this document.
|
|
||||||
|
|
||||||
Terminology & Basics
|
Terminology & Basics
|
||||||
------------------------
|
------------------------
|
||||||
@@ -79,7 +70,7 @@ Below are additional terms used throughout this document:
|
|||||||
_Reader/Writer_
|
_Reader/Writer_
|
||||||
|
|
||||||
: Code that reads WebP files is referred to as a _reader_, while code that
|
: Code that reads WebP files is referred to as a _reader_, while code that
|
||||||
writes them is referred to as a _writer_.
|
writes them is referred to as a _writer_.
|
||||||
|
|
||||||
_uint16_
|
_uint16_
|
||||||
|
|
||||||
@@ -101,10 +92,12 @@ _FourCC_
|
|||||||
_1-based_
|
_1-based_
|
||||||
|
|
||||||
: An unsigned integer field storing values offset by `-1`. e.g., Such a field
|
: An unsigned integer field storing values offset by `-1`. e.g., Such a field
|
||||||
would store value _25_ as _24_.
|
would store value _25_ as _24_.
|
||||||
|
|
||||||
RIFF file format
|
|
||||||
|
RIFF File Format
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The WebP file format is based on the RIFF (resource interchange file format)
|
The WebP file format is based on the RIFF (resource interchange file format)
|
||||||
document format.
|
document format.
|
||||||
|
|
||||||
@@ -144,7 +137,8 @@ _ChunkHeader('ABCD')_
|
|||||||
chunks that apply to any RIFF file format, while FourCCs specific to a file
|
chunks that apply to any RIFF file format, while FourCCs specific to a file
|
||||||
format are all lowercase. WebP does not follow this convention.
|
format are all lowercase. WebP does not follow this convention.
|
||||||
|
|
||||||
WebP file header
|
|
||||||
|
WebP File Header
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
@@ -164,8 +158,8 @@ WebP file header
|
|||||||
File Size: 32 bits (_uint32_)
|
File Size: 32 bits (_uint32_)
|
||||||
|
|
||||||
: The size of the file in bytes starting at offset 8. The maximum value of
|
: The size of the file in bytes starting at offset 8. The maximum value of
|
||||||
this field is 2^32 minus 10 bytes and thus the size of the whole file is at
|
this field is 2^32 minus 10 bytes and thus the size of the whole file is at
|
||||||
most 4GiB minus 2 bytes.
|
most 4GiB minus 2 bytes.
|
||||||
|
|
||||||
'WEBP': 32 bits
|
'WEBP': 32 bits
|
||||||
|
|
||||||
@@ -177,7 +171,8 @@ the 'WEBP' FourCC. The file SHOULD NOT contain anything after it. As the size
|
|||||||
of any chunk is even, the size given by the RIFF header is also even. The
|
of any chunk is even, the size given by the RIFF header is also even. The
|
||||||
contents of individual chunks will be described in the following sections.
|
contents of individual chunks will be described in the following sections.
|
||||||
|
|
||||||
Simple file format (lossy)
|
|
||||||
|
Simple File Format (Lossy)
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
This layout SHOULD be used if the image requires _lossy_ encoding and does not
|
This layout SHOULD be used if the image requires _lossy_ encoding and does not
|
||||||
@@ -215,7 +210,8 @@ width and height. That is assumed to be the width and height of the canvas.
|
|||||||
The VP8 specification describes how to decode the image into Y'CbCr
|
The VP8 specification describes how to decode the image into Y'CbCr
|
||||||
format. To convert to RGB, Rec. 601 SHOULD be used.
|
format. To convert to RGB, Rec. 601 SHOULD be used.
|
||||||
|
|
||||||
Simple file format (lossless)
|
|
||||||
|
Simple File Format (Lossless)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
**Note:** Older readers may not support files using the lossless format.
|
**Note:** Older readers may not support files using the lossless format.
|
||||||
@@ -253,7 +249,8 @@ The current specification of the VP8L bitstream can be found at
|
|||||||
contains the VP8L image width and height. That is assumed to be the width
|
contains the VP8L image width and height. That is assumed to be the width
|
||||||
and height of the canvas.
|
and height of the canvas.
|
||||||
|
|
||||||
Extended file format
|
|
||||||
|
Extended File Format
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
**Note:** Older readers may not support files using the extended format.
|
**Note:** Older readers may not support files using the extended format.
|
||||||
@@ -274,13 +271,15 @@ An extended format file consists of:
|
|||||||
|
|
||||||
* An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_
|
* An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_
|
||||||
|
|
||||||
For a _still image_, the _image data_ consists of a single frame, whereas for
|
For a _still image_, the _image data_ consists of a single frame, which is made
|
||||||
an _animated image_, it consists of multiple frames. More details about frames
|
up of:
|
||||||
can be found in the [Animation](#animation) section.
|
|
||||||
|
|
||||||
Moreover, each frame can be fragmented or non-fragmented, as will be described
|
* An optional [alpha subchunk](#alpha).
|
||||||
in the [Extended WebP file header](#extended_header) section. More details about
|
|
||||||
fragments can be found in the [Fragments](#fragments) section.
|
* A [bitstream subchunk](#bitstream-vp8vp8l).
|
||||||
|
|
||||||
|
For an _animated image_, the _image data_ consists of multiple frames. More
|
||||||
|
details about frames can be found in the [Animation](#animation) section.
|
||||||
|
|
||||||
All chunks SHOULD be placed in the same order as listed above. If a chunk
|
All chunks SHOULD be placed in the same order as listed above. If a chunk
|
||||||
appears in the wrong place, the file is invalid, but readers MAY parse the
|
appears in the wrong place, the file is invalid, but readers MAY parse the
|
||||||
@@ -302,7 +301,7 @@ Extended WebP file header:
|
|||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| ChunkHeader('VP8X') |
|
| ChunkHeader('VP8X') |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|Rsv|I|L|E|X|A|F| Reserved |
|
|Rsv|I|L|E|X|A|R| Reserved |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Canvas Width Minus One | ...
|
| Canvas Width Minus One | ...
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
@@ -320,7 +319,7 @@ ICC profile (I): 1 bit
|
|||||||
Alpha (L): 1 bit
|
Alpha (L): 1 bit
|
||||||
|
|
||||||
: Set if any of the frames of the image contain transparency information
|
: Set if any of the frames of the image contain transparency information
|
||||||
("alpha").
|
("alpha").
|
||||||
|
|
||||||
EXIF metadata (E): 1 bit
|
EXIF metadata (E): 1 bit
|
||||||
|
|
||||||
@@ -333,11 +332,11 @@ XMP metadata (X): 1 bit
|
|||||||
Animation (A): 1 bit
|
Animation (A): 1 bit
|
||||||
|
|
||||||
: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
|
: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
|
||||||
used to control the animation.
|
used to control the animation.
|
||||||
|
|
||||||
Image Fragmentation (F): 1 bit _\[status: experimental\]_
|
Reserved (R): 1 bit
|
||||||
|
|
||||||
: Set if any of the frames in the image are represented by fragments.
|
: SHOULD be `0`.
|
||||||
|
|
||||||
Reserved: 24 bits
|
Reserved: 24 bits
|
||||||
|
|
||||||
@@ -382,9 +381,9 @@ animation.
|
|||||||
Background Color: 32 bits (_uint32_)
|
Background Color: 32 bits (_uint32_)
|
||||||
|
|
||||||
: The default background color of the canvas in \[Blue, Green, Red, Alpha\]
|
: The default background color of the canvas in \[Blue, Green, Red, Alpha\]
|
||||||
byte order. This color MAY be used to fill the unused space on the canvas around
|
byte order. This color MAY be used to fill the unused space on the canvas
|
||||||
the frames, as well as the transparent pixels of the first frame. Background
|
around the frames, as well as the transparent pixels of the first frame.
|
||||||
color is also used when disposal method is `1`.
|
Background color is also used when disposal method is `1`.
|
||||||
|
|
||||||
**Note**:
|
**Note**:
|
||||||
|
|
||||||
@@ -394,6 +393,9 @@ color is also used when disposal method is `1`.
|
|||||||
* Viewer applications SHOULD treat the background color value as a hint, and
|
* Viewer applications SHOULD treat the background color value as a hint, and
|
||||||
are not required to use it.
|
are not required to use it.
|
||||||
|
|
||||||
|
* The canvas is cleared at the start of each loop. The background color MAY be
|
||||||
|
used to achieve this.
|
||||||
|
|
||||||
Loop Count: 16 bits (_uint16_)
|
Loop Count: 16 bits (_uint16_)
|
||||||
|
|
||||||
: The number of times to loop the animation. `0` means infinitely.
|
: The number of times to loop the animation. `0` means infinitely.
|
||||||
@@ -402,7 +404,6 @@ This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set.
|
|||||||
If the _Animation_ flag is not set and this chunk is present, it
|
If the _Animation_ flag is not set and this chunk is present, it
|
||||||
SHOULD be ignored.
|
SHOULD be ignored.
|
||||||
|
|
||||||
|
|
||||||
ANMF chunk:
|
ANMF chunk:
|
||||||
|
|
||||||
For animated images, this chunk contains information about a _single_ frame.
|
For animated images, this chunk contains information about a _single_ frame.
|
||||||
@@ -445,8 +446,8 @@ Frame Height Minus One: 24 bits (_uint24_)
|
|||||||
Frame Duration: 24 bits (_uint24_)
|
Frame Duration: 24 bits (_uint24_)
|
||||||
|
|
||||||
: The time to wait before displaying the next frame, in 1 millisecond units.
|
: The time to wait before displaying the next frame, in 1 millisecond units.
|
||||||
In particular, frame duration of 0 is useful when one wants to update multiple
|
In particular, frame duration of 0 is useful when one wants to update
|
||||||
areas of the canvas at once during the animation.
|
multiple areas of the canvas at once during the animation.
|
||||||
|
|
||||||
Reserved: 6 bits
|
Reserved: 6 bits
|
||||||
|
|
||||||
@@ -454,13 +455,13 @@ Reserved: 6 bits
|
|||||||
|
|
||||||
Blending method (B): 1 bit
|
Blending method (B): 1 bit
|
||||||
|
|
||||||
: Indicates how transparent pixels of _the current frame_ are to be blended with
|
: Indicates how transparent pixels of _the current frame_ are to be blended
|
||||||
corresponding pixels of the previous canvas:
|
with corresponding pixels of the previous canvas:
|
||||||
|
|
||||||
* `0`: Use alpha blending. After disposing of the previous frame, render the
|
* `0`: Use alpha blending. After disposing of the previous frame, render the
|
||||||
current frame on the canvas using [alpha-blending](#alpha-blending). If the
|
current frame on the canvas using [alpha-blending](#alpha-blending). If
|
||||||
current frame does not have an alpha channel, assume alpha value of 255,
|
the current frame does not have an alpha channel, assume alpha value of
|
||||||
effectively replacing the rectangle.
|
255, effectively replacing the rectangle.
|
||||||
|
|
||||||
* `1`: Do not blend. After disposing of the previous frame, render the
|
* `1`: Do not blend. After disposing of the previous frame, render the
|
||||||
current frame on the canvas by overwriting the rectangle covered by the
|
current frame on the canvas by overwriting the rectangle covered by the
|
||||||
@@ -468,13 +469,13 @@ corresponding pixels of the previous canvas:
|
|||||||
|
|
||||||
Disposal method (D): 1 bit
|
Disposal method (D): 1 bit
|
||||||
|
|
||||||
: Indicates how _the current frame_ is to be treated after it has been displayed
|
: Indicates how _the current frame_ is to be treated after it has been
|
||||||
(before rendering the next frame) on the canvas:
|
displayed (before rendering the next frame) on the canvas:
|
||||||
|
|
||||||
* `0`: Do not dispose. Leave the canvas as is.
|
* `0`: Do not dispose. Leave the canvas as is.
|
||||||
|
|
||||||
* `1`: Dispose to background color. Fill the _rectangle_ on the canvas covered
|
* `1`: Dispose to background color. Fill the _rectangle_ on the canvas
|
||||||
by the _current frame_ with background color specified in the
|
covered by the _current frame_ with background color specified in the
|
||||||
[ANIM chunk](#anim_chunk).
|
[ANIM chunk](#anim_chunk).
|
||||||
|
|
||||||
**Notes**:
|
**Notes**:
|
||||||
@@ -506,9 +507,7 @@ Disposal method (D): 1 bit
|
|||||||
|
|
||||||
Frame Data: _Chunk Size_ - `16` bytes
|
Frame Data: _Chunk Size_ - `16` bytes
|
||||||
|
|
||||||
: For a fragmented frame, it consists of multiple [fragment chunks](#fragments).
|
: Consists of:
|
||||||
|
|
||||||
: For a non-fragmented frame, it consists of:
|
|
||||||
|
|
||||||
* An optional [alpha subchunk](#alpha) for the frame.
|
* An optional [alpha subchunk](#alpha) for the frame.
|
||||||
|
|
||||||
@@ -519,49 +518,6 @@ Frame Data: _Chunk Size_ - `16` bytes
|
|||||||
**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
|
**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
|
||||||
_padded_ chunks as described by the [RIFF file format](#riff-file-format).
|
_padded_ chunks as described by the [RIFF file format](#riff-file-format).
|
||||||
|
|
||||||
#### Fragments _\[status: experimental\]_
|
|
||||||
|
|
||||||
For images that are represented by fragments, this chunk contains data for
|
|
||||||
a single fragment. If the _Image Fragmentation Flag_ is not set, then this chunk
|
|
||||||
SHOULD NOT be present.
|
|
||||||
|
|
||||||
0 1 2 3
|
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| ChunkHeader('FRGM') |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Fragment X | ...
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
... Fragment Y | Fragment Data |
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
|
|
||||||
Fragment X: 24 bits (_uint24_)
|
|
||||||
|
|
||||||
: The X coordinate of the upper left corner of the fragment is `Fragment X * 2`
|
|
||||||
|
|
||||||
Fragment Y: 24 bits (_uint24_)
|
|
||||||
|
|
||||||
: The Y coordinate of the upper left corner of the fragment is `Fragment Y * 2`
|
|
||||||
|
|
||||||
Fragment Data: _Chunk Size_ - `6` bytes
|
|
||||||
|
|
||||||
: It contains:
|
|
||||||
|
|
||||||
* An optional [alpha subchunk](#alpha) for the fragment.
|
|
||||||
* The [bitstream subchunk](#bitstream-vp8vp8l) for the fragment.
|
|
||||||
* An optional list of [unknown chunks](#unknown-chunks).
|
|
||||||
|
|
||||||
Note: The width and height of the fragment is obtained from the bitstream
|
|
||||||
subchunk.
|
|
||||||
|
|
||||||
The fragments of a frame SHOULD have the following properties:
|
|
||||||
|
|
||||||
* They collectively cover the whole frame.
|
|
||||||
|
|
||||||
* No pair of fragments have any overlapping region on the frame.
|
|
||||||
|
|
||||||
* No portion of any fragment should be located outside of the canvas.
|
|
||||||
|
|
||||||
#### Alpha
|
#### Alpha
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
@@ -579,8 +535,8 @@ Reserved (Rsv): 2 bits
|
|||||||
Pre-processing (P): 2 bits
|
Pre-processing (P): 2 bits
|
||||||
|
|
||||||
: These INFORMATIVE bits are used to signal the pre-processing that has
|
: These INFORMATIVE bits are used to signal the pre-processing that has
|
||||||
been performed during compression. The decoder can use this information to
|
been performed during compression. The decoder can use this information to
|
||||||
e.g. dither the values or smooth the gradients prior to display.
|
e.g. dither the values or smooth the gradients prior to display.
|
||||||
|
|
||||||
* `0`: no pre-processing
|
* `0`: no pre-processing
|
||||||
* `1`: level reduction
|
* `1`: level reduction
|
||||||
@@ -643,8 +599,8 @@ Alpha bitstream: _Chunk Size_ - `1` bytes
|
|||||||
|
|
||||||
: Encoded alpha bitstream.
|
: Encoded alpha bitstream.
|
||||||
|
|
||||||
This optional chunk contains encoded alpha data for this frame/fragment. A
|
This optional chunk contains encoded alpha data for this frame. A frame
|
||||||
frame/fragment containing a 'VP8L' chunk SHOULD NOT contain this chunk.
|
containing a 'VP8L' chunk SHOULD NOT contain this chunk.
|
||||||
|
|
||||||
**Rationale**: The transparency information is already part of the 'VP8L'
|
**Rationale**: The transparency information is already part of the 'VP8L'
|
||||||
chunk.
|
chunk.
|
||||||
@@ -675,15 +631,15 @@ compression method is '0') or compressed using the lossless format
|
|||||||
|
|
||||||
#### Bitstream (VP8/VP8L)
|
#### Bitstream (VP8/VP8L)
|
||||||
|
|
||||||
This chunk contains compressed bitstream data for a single frame/fragment.
|
This chunk contains compressed bitstream data for a single frame.
|
||||||
|
|
||||||
A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
|
A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
|
||||||
significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
|
significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
|
||||||
"VP8L" as its tag.
|
"VP8L" as its tag.
|
||||||
|
|
||||||
The formats of VP8 and VP8L chunks are as described in sections
|
The formats of VP8 and VP8L chunks are as described in sections
|
||||||
[Simple file format (lossy)](#simple-file-format-lossy)
|
[Simple File Format (Lossy)](#simple-file-format-lossy)
|
||||||
and [Simple file format (lossless)](#simple-file-format-lossless) respectively.
|
and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively.
|
||||||
|
|
||||||
#### Color profile
|
#### Color profile
|
||||||
|
|
||||||
@@ -731,7 +687,6 @@ EXIF Metadata: _Chunk Size_ bytes
|
|||||||
|
|
||||||
: image metadata in EXIF format.
|
: image metadata in EXIF format.
|
||||||
|
|
||||||
|
|
||||||
XMP chunk:
|
XMP chunk:
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
@@ -762,47 +717,17 @@ A file MAY contain unknown chunks:
|
|||||||
|
|
||||||
* At the end of the file as described in [Extended WebP file
|
* At the end of the file as described in [Extended WebP file
|
||||||
header](#extended_header) section.
|
header](#extended_header) section.
|
||||||
* At the end of FRGM and ANMF chunks as described in [Fragments](#fragments)
|
* At the end of ANMF chunks as described in the
|
||||||
and [Animation](#animation) sections.
|
[Animation](#animation) section.
|
||||||
|
|
||||||
Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
|
Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
|
||||||
original order (unless they specifically intend to modify these chunks).
|
original order (unless they specifically intend to modify these chunks).
|
||||||
|
|
||||||
### Assembling the Canvas from fragments/frames
|
### Assembling the Canvas from frames
|
||||||
|
|
||||||
Here we provide an overview of how a reader should assemble a canvas in case
|
Here we provide an overview of how a reader should assemble a canvas in the
|
||||||
of a fragmented-image and in case of an animated image. The notation
|
case of an animated image. The notation _VP8X.field_ means the field in the
|
||||||
_VP8X.field_ means the field in the 'VP8X' chunk with the same description.
|
'VP8X' chunk with the same description.
|
||||||
|
|
||||||
Displaying a _fragmented image_ canvas MUST be equivalent to the following
|
|
||||||
pseudocode: _\[status: experimental\]_
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
assert VP8X.flags.hasFragments
|
|
||||||
canvas ← new black image of size VP8X.canvasWidth x VP8X.canvasHeight.
|
|
||||||
frgm_params ← nil
|
|
||||||
for chunk in image_data:
|
|
||||||
assert chunk.tag is "FRGM"
|
|
||||||
frgm_params.fragmentX = Fragment X
|
|
||||||
frgm_params.fragmentY = Fragment Y
|
|
||||||
for subchunk in 'Fragment Data':
|
|
||||||
if subchunk.tag == "ALPH":
|
|
||||||
assert alpha subchunks not found in 'Fragment Data' earlier
|
|
||||||
frgm_params.alpha = alpha_data
|
|
||||||
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
|
|
||||||
assert bitstream subchunks not found in 'Fragment Data' earlier
|
|
||||||
frgm_params.bitstream = bitstream_data
|
|
||||||
frgm_params.fragmentWidth = Width extracted from bitstream subchunk
|
|
||||||
frgm_params.fragmentHeight = Height extracted from bitstream subchunk
|
|
||||||
assert VP8X.canvasWidth >=
|
|
||||||
frgm_params.fragmentX + frgm_params.fragmentWidth
|
|
||||||
assert VP8X.canvasHeight >=
|
|
||||||
frgm_params.fragmentY + frgm_params.fragmentHeight
|
|
||||||
assert fragment has the properties mentioned in "Image Fragments" section.
|
|
||||||
render fragment with frame_params.alpha and frame_params.bitstream on canvas
|
|
||||||
with top-left corner in (frgm_params.fragmentX, frgm_params.fragmentY).
|
|
||||||
canvas contains the decoded canvas.
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Displaying an _animated image_ canvas MUST be equivalent to the following
|
Displaying an _animated image_ canvas MUST be equivalent to the following
|
||||||
pseudocode:
|
pseudocode:
|
||||||
@@ -810,28 +735,25 @@ pseudocode:
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
assert VP8X.flags.hasAnimation
|
assert VP8X.flags.hasAnimation
|
||||||
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
|
canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
|
||||||
background color ANIM.background_color.
|
background color ANIM.background_color.
|
||||||
loop_count ← ANIM.loopCount
|
loop_count ← ANIM.loopCount
|
||||||
dispose_method ← ANIM.disposeMethod
|
dispose_method ← ANIM.disposeMethod
|
||||||
if loop_count == 0:
|
if loop_count == 0:
|
||||||
loop_count = ∞
|
loop_count = ∞
|
||||||
frame_params ← nil
|
frame_params ← nil
|
||||||
for loop = 0, ..., loop_count - 1
|
assert next chunk in image_data is ANMF
|
||||||
assert next chunk in image_data is ANMF
|
for loop = 0..loop_count - 1
|
||||||
|
clear canvas to ANIM.background_color or application defined color
|
||||||
|
until eof or non-ANMF chunk
|
||||||
frame_params.frameX = Frame X
|
frame_params.frameX = Frame X
|
||||||
frame_params.frameY = Frame Y
|
frame_params.frameY = Frame Y
|
||||||
frame_params.frameWidth = Frame Width Minus One + 1
|
frame_params.frameWidth = Frame Width Minus One + 1
|
||||||
frame_params.frameHeight = Frame Height Minus One + 1
|
frame_params.frameHeight = Frame Height Minus One + 1
|
||||||
frame_params.frameDuration = Frame Duration
|
frame_params.frameDuration = Frame Duration
|
||||||
assert VP8X.canvasWidth >= frame_params.frameX + frame_params.frameWidth
|
frame_right = frame_params.frameX + frame_params.frameWidth
|
||||||
assert VP8X.canvasHeight >= frame_params.frameY + frame_params.frameHeight
|
frame_bottom = frame_params.frameY + frame_params.frameHeight
|
||||||
if VP8X.flags.hasFragments and first subchunk in 'Frame Data' is FRGM
|
assert VP8X.canvasWidth >= frame_right
|
||||||
// Fragmented frame.
|
assert VP8X.canvasHeight >= frame_bottom
|
||||||
frame_params.{bitstream,alpha} = canvas decoded from subchunks in
|
|
||||||
'Frame Data' as per the pseudocode for
|
|
||||||
_fragmented image_ above.
|
|
||||||
else
|
|
||||||
// Non-fragmented frame.
|
|
||||||
for subchunk in 'Frame Data':
|
for subchunk in 'Frame Data':
|
||||||
if subchunk.tag == "ALPH":
|
if subchunk.tag == "ALPH":
|
||||||
assert alpha subchunks not found in 'Frame Data' earlier
|
assert alpha subchunks not found in 'Frame Data' earlier
|
||||||
@@ -839,14 +761,15 @@ for loop = 0, ..., loop_count - 1
|
|||||||
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
|
else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
|
||||||
assert bitstream subchunks not found in 'Frame Data' earlier
|
assert bitstream subchunks not found in 'Frame Data' earlier
|
||||||
frame_params.bitstream = bitstream_data
|
frame_params.bitstream = bitstream_data
|
||||||
render frame with frame_params.alpha and frame_params.bitstream on canvas
|
render frame with frame_params.alpha and frame_params.bitstream on
|
||||||
with top-left corner in (frame_params.frameX, frame_params.frameY), using
|
canvas with top-left corner at (frame_params.frameX,
|
||||||
dispose method dispose_method.
|
frame_params.frameY), using dispose method dispose_method.
|
||||||
Show the contents of the image for frame_params.frameDuration * 1ms.
|
canvas contains the decoded image.
|
||||||
canvas contains the decoded canvas.
|
Show the contents of the canvas for frame_params.frameDuration * 1ms.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Example file layouts
|
|
||||||
|
Example File Layouts
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
A lossy encoded image with alpha may look as follows:
|
A lossy encoded image with alpha may look as follows:
|
||||||
@@ -878,17 +801,6 @@ RIFF/WEBP
|
|||||||
+- XMP (metadata)
|
+- XMP (metadata)
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A fragmented image may look as follows:
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
RIFF/WEBP
|
|
||||||
+- VP8X (descriptions of features used)
|
|
||||||
+- FRGM (fragment1 parameters + data)
|
|
||||||
+- FRGM (fragment2 parameters + data)
|
|
||||||
+- FRGM (fragment3 parameters + data)
|
|
||||||
+- FRGM (fragment4 parameters + data)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
An animated image with EXIF metadata may look as follows:
|
An animated image with EXIF metadata may look as follows:
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -903,7 +815,8 @@ RIFF/WEBP
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
[vp8spec]: http://tools.ietf.org/html/rfc6386
|
[vp8spec]: http://tools.ietf.org/html/rfc6386
|
||||||
[webpllspec]: https://gerrit.chromium.org/gerrit/gitweb?p=webm/libwebp.git;a=blob;f=doc/webp-lossless-bitstream-spec.txt;hb=master
|
[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
|
||||||
[iccspec]: http://www.color.org/icc_specs2.xalter
|
[iccspec]: http://www.color.org/icc_specs2.xalter
|
||||||
[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
|
[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
|
||||||
|
[rfc 1166]: http://tools.ietf.org/html/rfc1166
|
||||||
[rfc 2119]: http://tools.ietf.org/html/rfc2119
|
[rfc 2119]: http://tools.ietf.org/html/rfc2119
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Specification for WebP Lossless Bitstream
|
|||||||
|
|
||||||
_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
|
_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
|
||||||
|
|
||||||
|
Paragraphs marked as \[AMENDED\] were amended on 2014-09-16.
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
--------
|
--------
|
||||||
@@ -172,8 +173,8 @@ It should be set to 0 when all alpha values are 255 in the picture, and
|
|||||||
int alpha_is_used = ReadBits(1);
|
int alpha_is_used = ReadBits(1);
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The version_number is a 3 bit code that must be discarded by the decoder
|
The version_number is a 3 bit code that must be set to 0. Any other value
|
||||||
at this time. Complying encoders write a 3-bit value 0.
|
should be treated as an error. \[AMENDED\]
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
int version_number = ReadBits(3);
|
int version_number = ReadBits(3);
|
||||||
@@ -330,7 +331,7 @@ uint32 Select(uint32 L, uint32 T, uint32 TL) {
|
|||||||
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
|
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
|
||||||
|
|
||||||
// Return either left or top, the one closer to the prediction.
|
// Return either left or top, the one closer to the prediction.
|
||||||
if (pL <= pT) {
|
if (pL < pT) { // \[AMENDED\]
|
||||||
return L;
|
return L;
|
||||||
} else {
|
} else {
|
||||||
return T;
|
return T;
|
||||||
@@ -428,6 +429,11 @@ int8 ColorTransformDelta(int8 t, int8 c) {
|
|||||||
}
|
}
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A conversion from the 8-bit unsigned representation (uint8) to the 8-bit
|
||||||
|
signed one (int8) is required before calling ColorTransformDelta().
|
||||||
|
It should be performed using 8-bit two's complement (that is: uint8 range
|
||||||
|
\[128-255\] is mapped to the \[-128, -1\] range of its converted int8 value).
|
||||||
|
|
||||||
The multiplication is to be done using more precision (with at least
|
The multiplication is to be done using more precision (with at least
|
||||||
16-bit dynamics). The sign extension property of the shift operation
|
16-bit dynamics). The sign extension property of the shift operation
|
||||||
does not matter here: only the lowest 8 bits are used from the result,
|
does not matter here: only the lowest 8 bits are used from the result,
|
||||||
@@ -542,6 +548,9 @@ color.
|
|||||||
argb = color_table[GREEN(argb)];
|
argb = color_table[GREEN(argb)];
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the index is equal or larger than color_table_size, the argb color value
|
||||||
|
should be set to 0x00000000 (transparent black). \[AMENDED\]
|
||||||
|
|
||||||
When the color table is small (equal to or less than 16 colors), several
|
When the color table is small (equal to or less than 16 colors), several
|
||||||
pixels are bundled into a single pixel. The pixel bundling packs several
|
pixels are bundled into a single pixel. The pixel bundling packs several
|
||||||
(2, 4, or 8) pixels into a single pixel, reducing the image width
|
(2, 4, or 8) pixels into a single pixel, reducing the image width
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
LOCAL_PATH := $(call my-dir)
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libexample_util
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
@@ -12,26 +15,27 @@ LOCAL_MODULE := example_util
|
|||||||
|
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# cwebp
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
# Note: to enable jpeg/png encoding the sources from AOSP can be used with
|
# Note: to enable jpeg/png encoding the sources from AOSP can be used with
|
||||||
# minor modification to their Android.mk files.
|
# minor modification to their Android.mk files.
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
cwebp.c \
|
cwebp.c \
|
||||||
jpegdec.c \
|
|
||||||
metadata.c \
|
|
||||||
pngdec.c \
|
|
||||||
tiffdec.c \
|
|
||||||
webpdec.c \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
LOCAL_STATIC_LIBRARIES := example_util webp
|
LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webp
|
||||||
|
|
||||||
LOCAL_MODULE := cwebp
|
LOCAL_MODULE := cwebp
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# dwebp
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
@@ -39,8 +43,40 @@ LOCAL_SRC_FILES := \
|
|||||||
|
|
||||||
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
LOCAL_STATIC_LIBRARIES := example_util webp
|
LOCAL_STATIC_LIBRARIES := example_util imagedec imageenc webp
|
||||||
|
|
||||||
LOCAL_MODULE := dwebp
|
LOCAL_MODULE := dwebp
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# webpmux
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
webpmux.c \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
|
LOCAL_STATIC_LIBRARIES := example_util imageio_util webpmux webp
|
||||||
|
|
||||||
|
LOCAL_MODULE := webpmux_example
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# img2webp
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
img2webp.c \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
|
LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webp
|
||||||
|
|
||||||
|
LOCAL_MODULE := img2webp_example
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|||||||
@@ -1,54 +1,75 @@
|
|||||||
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
|
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||||
|
|
||||||
bin_PROGRAMS = dwebp cwebp
|
bin_PROGRAMS = dwebp cwebp
|
||||||
if BUILD_VWEBP
|
if BUILD_ANIMDIFF
|
||||||
bin_PROGRAMS += vwebp
|
noinst_PROGRAMS = anim_diff
|
||||||
|
endif
|
||||||
|
if BUILD_GIF2WEBP
|
||||||
|
bin_PROGRAMS += gif2webp
|
||||||
|
endif
|
||||||
|
if BUILD_IMG2WEBP
|
||||||
|
bin_PROGRAMS += img2webp
|
||||||
endif
|
endif
|
||||||
if WANT_MUX
|
if WANT_MUX
|
||||||
bin_PROGRAMS += webpmux
|
bin_PROGRAMS += webpmux
|
||||||
endif
|
endif
|
||||||
|
if BUILD_VWEBP
|
||||||
if BUILD_GIF2WEBP
|
bin_PROGRAMS += vwebp
|
||||||
bin_PROGRAMS += gif2webp
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libexampleutil.la
|
noinst_LTLIBRARIES = libexample_util.la
|
||||||
|
|
||||||
libexampleutil_la_SOURCES = example_util.c example_util.h stopwatch.h
|
libexample_util_la_SOURCES = example_util.c example_util.h
|
||||||
|
libexample_util_la_LIBADD = ../src/libwebp.la
|
||||||
|
|
||||||
|
anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
|
||||||
|
anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
|
||||||
|
anim_diff_LDADD = ../src/demux/libwebpdemux.la
|
||||||
|
anim_diff_LDADD += libexample_util.la ../imageio/libimageio_util.la
|
||||||
|
anim_diff_LDADD += $(GIF_LIBS) -lm
|
||||||
|
|
||||||
|
cwebp_SOURCES = cwebp.c stopwatch.h
|
||||||
|
cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
|
cwebp_LDADD = libexample_util.la ../imageio/libimageio_util.la
|
||||||
|
cwebp_LDADD += ../imageio/libimagedec.la ../src/libwebp.la
|
||||||
|
cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
|
||||||
|
|
||||||
dwebp_SOURCES = dwebp.c stopwatch.h
|
dwebp_SOURCES = dwebp.c stopwatch.h
|
||||||
dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
dwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
|
dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
|
||||||
dwebp_LDADD = libexampleutil.la $(PNG_LIBS) $(JPEG_LIBS)
|
dwebp_LDADD = libexample_util.la
|
||||||
|
dwebp_LDADD += ../imageio/libimagedec.la
|
||||||
|
dwebp_LDADD += ../imageio/libimageenc.la
|
||||||
|
dwebp_LDADD += ../imageio/libimageio_util.la
|
||||||
|
dwebp_LDADD += ../src/libwebp.la
|
||||||
|
dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
|
||||||
|
|
||||||
cwebp_SOURCES = cwebp.c metadata.c metadata.h stopwatch.h
|
gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h
|
||||||
cwebp_SOURCES += jpegdec.c jpegdec.h
|
|
||||||
cwebp_SOURCES += pngdec.c pngdec.h
|
|
||||||
cwebp_SOURCES += tiffdec.c tiffdec.h
|
|
||||||
cwebp_SOURCES += webpdec.c webpdec.h
|
|
||||||
cwebp_SOURCES += wicdec.c wicdec.h
|
|
||||||
cwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
|
||||||
cwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
|
|
||||||
cwebp_LDADD = libexampleutil.la ../src/libwebp.la
|
|
||||||
cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
|
|
||||||
|
|
||||||
gif2webp_SOURCES = gif2webp.c gif2webp_util.c
|
|
||||||
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
|
gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
|
||||||
gif2webp_LDADD = libexampleutil.la ../src/mux/libwebpmux.la ../src/libwebp.la
|
gif2webp_LDADD = libexample_util.la ../imageio/libimageio_util.la
|
||||||
gif2webp_LDADD += $(GIF_LIBS)
|
gif2webp_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la $(GIF_LIBS)
|
||||||
|
|
||||||
webpmux_SOURCES = webpmux.c
|
|
||||||
webpmux_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
|
||||||
webpmux_LDADD = libexampleutil.la ../src/mux/libwebpmux.la ../src/libwebp.la
|
|
||||||
|
|
||||||
vwebp_SOURCES = vwebp.c
|
vwebp_SOURCES = vwebp.c
|
||||||
vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GL_INCLUDES)
|
vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GL_INCLUDES)
|
||||||
vwebp_LDADD = libexampleutil.la ../src/demux/libwebpdemux.la $(GL_LIBS)
|
vwebp_LDADD = libexample_util.la ../imageio/libimageio_util.la
|
||||||
|
vwebp_LDADD += ../src/demux/libwebpdemux.la $(GL_LIBS)
|
||||||
|
|
||||||
|
webpmux_SOURCES = webpmux.c
|
||||||
|
webpmux_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
|
webpmux_LDADD = libexample_util.la ../imageio/libimageio_util.la
|
||||||
|
webpmux_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la
|
||||||
|
|
||||||
|
img2webp_SOURCES = img2webp.c
|
||||||
|
img2webp_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
|
img2webp_LDADD = libexample_util.la ../imageio/libimageio_util.la
|
||||||
|
img2webp_LDADD += ../imageio/libimagedec.la
|
||||||
|
img2webp_LDADD += ../src/mux/libwebpmux.la ../src/libwebp.la
|
||||||
|
img2webp_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
|
||||||
|
|
||||||
if BUILD_LIBWEBPDECODER
|
if BUILD_LIBWEBPDECODER
|
||||||
dwebp_LDADD += ../src/libwebpdecoder.la
|
anim_diff_LDADD += ../src/libwebpdecoder.la
|
||||||
vwebp_LDADD += ../src/libwebpdecoder.la
|
vwebp_LDADD += ../src/libwebpdecoder.la
|
||||||
else
|
else
|
||||||
dwebp_LDADD += ../src/libwebp.la
|
anim_diff_LDADD += ../src/libwebp.la
|
||||||
vwebp_LDADD += ../src/libwebp.la
|
vwebp_LDADD += ../src/libwebp.la
|
||||||
endif
|
endif
|
||||||
|
|||||||
303
examples/anim_diff.c
Normal file
303
examples/anim_diff.c
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Checks if given pair of animated GIF/WebP images are identical:
|
||||||
|
// That is: their reconstructed canvases match pixel-by-pixel and their other
|
||||||
|
// animation properties (loop count etc) also match.
|
||||||
|
//
|
||||||
|
// example: anim_diff foo.gif bar.webp
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> // for 'strtod'.
|
||||||
|
#include <string.h> // for 'strcmp'.
|
||||||
|
|
||||||
|
#include "./anim_util.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns true if 'a + b' will overflow.
|
||||||
|
static int AdditionWillOverflow(int a, int b) {
|
||||||
|
return (b > 0) && (a > INT_MAX - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FramesAreEqual(const uint8_t* const rgba1,
|
||||||
|
const uint8_t* const rgba2, int width, int height) {
|
||||||
|
const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
|
||||||
|
return !memcmp(rgba1, rgba2, stride * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
|
||||||
|
int max_allowed_diff) {
|
||||||
|
const int src_a = (src >> 24) & 0xff;
|
||||||
|
const int src_r = (src >> 16) & 0xff;
|
||||||
|
const int src_g = (src >> 8) & 0xff;
|
||||||
|
const int src_b = (src >> 0) & 0xff;
|
||||||
|
const int dst_a = (dst >> 24) & 0xff;
|
||||||
|
const int dst_r = (dst >> 16) & 0xff;
|
||||||
|
const int dst_g = (dst >> 8) & 0xff;
|
||||||
|
const int dst_b = (dst >> 0) & 0xff;
|
||||||
|
|
||||||
|
return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
|
||||||
|
(abs(src_a - dst_a) <= max_allowed_diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FramesAreSimilar(const uint8_t* const rgba1,
|
||||||
|
const uint8_t* const rgba2,
|
||||||
|
int width, int height, int max_allowed_diff) {
|
||||||
|
int i, j;
|
||||||
|
assert(max_allowed_diff > 0);
|
||||||
|
for (j = 0; j < height; ++j) {
|
||||||
|
for (i = 0; i < width; ++i) {
|
||||||
|
const int stride = width * 4;
|
||||||
|
const size_t offset = j * stride + i;
|
||||||
|
if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimize number of frames by combining successive frames that have at max
|
||||||
|
// 'max_diff' difference per channel between corresponding pixels.
|
||||||
|
static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 1; i < img->num_frames; ++i) {
|
||||||
|
DecodedFrame* const frame1 = &img->frames[i - 1];
|
||||||
|
DecodedFrame* const frame2 = &img->frames[i];
|
||||||
|
const uint8_t* const rgba1 = frame1->rgba;
|
||||||
|
const uint8_t* const rgba2 = frame2->rgba;
|
||||||
|
int should_merge_frames = 0;
|
||||||
|
// If merging frames will result in integer overflow for 'duration',
|
||||||
|
// skip merging.
|
||||||
|
if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
|
||||||
|
if (max_diff > 0) {
|
||||||
|
should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
|
||||||
|
img->canvas_height, max_diff);
|
||||||
|
} else {
|
||||||
|
should_merge_frames =
|
||||||
|
FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
|
||||||
|
}
|
||||||
|
if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame.
|
||||||
|
frame1->duration += frame2->duration;
|
||||||
|
if (i + 1 < img->num_frames) {
|
||||||
|
memmove(&img->frames[i], &img->frames[i + 1],
|
||||||
|
(img->num_frames - i - 1) * sizeof(*img->frames));
|
||||||
|
}
|
||||||
|
--img->num_frames;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
|
||||||
|
if (a != b) {
|
||||||
|
fprintf(stderr, "%s: %d vs %d\n", output_str, a, b);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
|
||||||
|
if (premultiply) {
|
||||||
|
const int alpha1 = (bg1 >> 24) & 0xff;
|
||||||
|
const int alpha2 = (bg2 >> 24) & 0xff;
|
||||||
|
if (alpha1 == 0 && alpha2 == 0) return 1;
|
||||||
|
}
|
||||||
|
if (bg1 != bg2) {
|
||||||
|
fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
|
||||||
|
bg1, bg2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: As long as frame durations and reconstructed frames are identical, it
|
||||||
|
// is OK for other aspects like offsets, dispose/blend method to vary.
|
||||||
|
static int CompareAnimatedImagePair(const AnimatedImage* const img1,
|
||||||
|
const AnimatedImage* const img2,
|
||||||
|
int premultiply,
|
||||||
|
double min_psnr) {
|
||||||
|
int ok = 1;
|
||||||
|
const int is_multi_frame_image = (img1->num_frames > 1);
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
ok = CompareValues(img1->canvas_width, img2->canvas_width,
|
||||||
|
"Canvas width mismatch") && ok;
|
||||||
|
ok = CompareValues(img1->canvas_height, img2->canvas_height,
|
||||||
|
"Canvas height mismatch") && ok;
|
||||||
|
ok = CompareValues(img1->num_frames, img2->num_frames,
|
||||||
|
"Frame count mismatch") && ok;
|
||||||
|
if (!ok) return 0; // These are fatal failures, can't proceed.
|
||||||
|
|
||||||
|
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||||||
|
ok = CompareValues(img1->loop_count, img2->loop_count,
|
||||||
|
"Loop count mismatch") && ok;
|
||||||
|
ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
|
||||||
|
premultiply) && ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < img1->num_frames; ++i) {
|
||||||
|
// Pixel-by-pixel comparison.
|
||||||
|
const uint8_t* const rgba1 = img1->frames[i].rgba;
|
||||||
|
const uint8_t* const rgba2 = img2->frames[i].rgba;
|
||||||
|
int max_diff;
|
||||||
|
double psnr;
|
||||||
|
if (is_multi_frame_image) { // Check relevant for multi-frame images only.
|
||||||
|
const char format[] = "Frame #%d, duration mismatch";
|
||||||
|
char tmp[sizeof(format) + 8];
|
||||||
|
ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0);
|
||||||
|
ok = ok && CompareValues(img1->frames[i].duration,
|
||||||
|
img2->frames[i].duration, tmp);
|
||||||
|
}
|
||||||
|
GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height,
|
||||||
|
premultiply, &max_diff, &psnr);
|
||||||
|
if (min_psnr > 0.) {
|
||||||
|
if (psnr < min_psnr) {
|
||||||
|
fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
|
||||||
|
psnr, min_psnr);
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (max_diff != 0) {
|
||||||
|
fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff);
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(void) {
|
||||||
|
printf("Usage: anim_diff <image1> <image2> [options]\n");
|
||||||
|
printf("\nOptions:\n");
|
||||||
|
printf(" -dump_frames <folder> dump decoded frames in PAM format\n");
|
||||||
|
printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
|
||||||
|
printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
|
||||||
|
printf(" premultiplied before comparison\n");
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
printf(" -max_diff <int> ..... maximum allowed difference per channel "
|
||||||
|
" between corresponding pixels in subsequent"
|
||||||
|
" frames\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
int return_code = -1;
|
||||||
|
int dump_frames = 0;
|
||||||
|
const char* dump_folder = NULL;
|
||||||
|
double min_psnr = 0.;
|
||||||
|
int got_input1 = 0;
|
||||||
|
int got_input2 = 0;
|
||||||
|
int premultiply = 1;
|
||||||
|
int max_diff = 0;
|
||||||
|
int i, c;
|
||||||
|
const char* files[2] = { NULL, NULL };
|
||||||
|
AnimatedImage images[2];
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = 1; c < argc; ++c) {
|
||||||
|
int parse_error = 0;
|
||||||
|
if (!strcmp(argv[c], "-dump_frames")) {
|
||||||
|
if (c < argc - 1) {
|
||||||
|
dump_frames = 1;
|
||||||
|
dump_folder = argv[++c];
|
||||||
|
} else {
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[c], "-min_psnr")) {
|
||||||
|
if (c < argc - 1) {
|
||||||
|
const char* const v = argv[++c];
|
||||||
|
char* end = NULL;
|
||||||
|
const double d = strtod(v, &end);
|
||||||
|
if (end == v) {
|
||||||
|
parse_error = 1;
|
||||||
|
fprintf(stderr, "Error! '%s' is not a floating point number.\n", v);
|
||||||
|
}
|
||||||
|
min_psnr = d;
|
||||||
|
} else {
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[c], "-raw_comparison")) {
|
||||||
|
premultiply = 0;
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
} else if (!strcmp(argv[c], "-max_diff")) {
|
||||||
|
if (c < argc - 1) {
|
||||||
|
const char* const v = argv[++c];
|
||||||
|
char* end = NULL;
|
||||||
|
const int n = (int)strtol(v, &end, 10);
|
||||||
|
if (end == v) {
|
||||||
|
parse_error = 1;
|
||||||
|
fprintf(stderr, "Error! '%s' is not an integer.\n", v);
|
||||||
|
}
|
||||||
|
max_diff = n;
|
||||||
|
} else {
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if (!got_input1) {
|
||||||
|
files[0] = argv[c];
|
||||||
|
got_input1 = 1;
|
||||||
|
} else if (!got_input2) {
|
||||||
|
files[1] = argv[c];
|
||||||
|
got_input2 = 1;
|
||||||
|
} else {
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parse_error) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!got_input2) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dump_frames) {
|
||||||
|
printf("Dumping decoded frames in: %s\n", dump_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(images, 0, sizeof(images));
|
||||||
|
for (i = 0; i < 2; ++i) {
|
||||||
|
printf("Decoding file: %s\n", files[i]);
|
||||||
|
if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
|
||||||
|
fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]);
|
||||||
|
return_code = -2;
|
||||||
|
goto End;
|
||||||
|
} else {
|
||||||
|
MinimizeAnimationFrames(&images[i], max_diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompareAnimatedImagePair(&images[0], &images[1],
|
||||||
|
premultiply, min_psnr)) {
|
||||||
|
fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
|
||||||
|
return_code = -3;
|
||||||
|
} else {
|
||||||
|
printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
|
||||||
|
return_code = 0;
|
||||||
|
}
|
||||||
|
End:
|
||||||
|
ClearAnimatedImage(&images[0]);
|
||||||
|
ClearAnimatedImage(&images[1]);
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
773
examples/anim_util.c
Normal file
773
examples/anim_util.c
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utilities for animated images
|
||||||
|
|
||||||
|
#include "./anim_util.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef WEBP_HAVE_GIF
|
||||||
|
#include <gif_lib.h>
|
||||||
|
#endif
|
||||||
|
#include "webp/format_constants.h"
|
||||||
|
#include "webp/decode.h"
|
||||||
|
#include "webp/demux.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const int kNumChannels = 4;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Common utilities.
|
||||||
|
|
||||||
|
// Returns true if the frame covers the full canvas.
|
||||||
|
static int IsFullFrame(int width, int height,
|
||||||
|
int canvas_width, int canvas_height) {
|
||||||
|
return (width == canvas_width && height == canvas_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CheckSizeForOverflow(uint64_t size) {
|
||||||
|
return (size == (size_t)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) {
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t* mem = NULL;
|
||||||
|
DecodedFrame* frames = NULL;
|
||||||
|
const uint64_t rgba_size =
|
||||||
|
(uint64_t)image->canvas_width * kNumChannels * image->canvas_height;
|
||||||
|
const uint64_t total_size = (uint64_t)num_frames * rgba_size * sizeof(*mem);
|
||||||
|
const uint64_t total_frame_size = (uint64_t)num_frames * sizeof(*frames);
|
||||||
|
if (!CheckSizeForOverflow(total_size) ||
|
||||||
|
!CheckSizeForOverflow(total_frame_size)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mem = (uint8_t*)malloc((size_t)total_size);
|
||||||
|
frames = (DecodedFrame*)malloc((size_t)total_frame_size);
|
||||||
|
|
||||||
|
if (mem == NULL || frames == NULL) {
|
||||||
|
free(mem);
|
||||||
|
free(frames);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(image->raw_mem);
|
||||||
|
image->num_frames = num_frames;
|
||||||
|
image->frames = frames;
|
||||||
|
for (i = 0; i < num_frames; ++i) {
|
||||||
|
frames[i].rgba = mem + i * rgba_size;
|
||||||
|
frames[i].duration = 0;
|
||||||
|
frames[i].is_key_frame = 0;
|
||||||
|
}
|
||||||
|
image->raw_mem = mem;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearAnimatedImage(AnimatedImage* const image) {
|
||||||
|
if (image != NULL) {
|
||||||
|
free(image->raw_mem);
|
||||||
|
free(image->frames);
|
||||||
|
image->num_frames = 0;
|
||||||
|
image->frames = NULL;
|
||||||
|
image->raw_mem = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the canvas to transparent.
|
||||||
|
static void ZeroFillCanvas(uint8_t* rgba,
|
||||||
|
uint32_t canvas_width, uint32_t canvas_height) {
|
||||||
|
memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear given frame rectangle to transparent.
|
||||||
|
static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
|
||||||
|
int y_offset, int width, int height) {
|
||||||
|
int j;
|
||||||
|
assert(width * kNumChannels <= rgba_stride);
|
||||||
|
rgba += y_offset * rgba_stride + x_offset * kNumChannels;
|
||||||
|
for (j = 0; j < height; ++j) {
|
||||||
|
memset(rgba, 0, width * kNumChannels);
|
||||||
|
rgba += rgba_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy width * height pixels from 'src' to 'dst'.
|
||||||
|
static void CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||||
|
uint32_t width, uint32_t height) {
|
||||||
|
assert(src != NULL && dst != NULL);
|
||||||
|
memcpy(dst, src, width * kNumChannels * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
|
||||||
|
static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
|
||||||
|
int x_offset, int y_offset,
|
||||||
|
int width, int height) {
|
||||||
|
int j;
|
||||||
|
const int width_in_bytes = width * kNumChannels;
|
||||||
|
const size_t offset = y_offset * stride + x_offset * kNumChannels;
|
||||||
|
assert(width_in_bytes <= stride);
|
||||||
|
src += offset;
|
||||||
|
dst += offset;
|
||||||
|
for (j = 0; j < height; ++j) {
|
||||||
|
memcpy(dst, src, width_in_bytes);
|
||||||
|
src += stride;
|
||||||
|
dst += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canonicalize all transparent pixels to transparent black to aid comparison.
|
||||||
|
static void CleanupTransparentPixels(uint32_t* rgba,
|
||||||
|
uint32_t width, uint32_t height) {
|
||||||
|
const uint32_t* const rgba_end = rgba + width * height;
|
||||||
|
while (rgba < rgba_end) {
|
||||||
|
const uint8_t alpha = (*rgba >> 24) & 0xff;
|
||||||
|
if (alpha == 0) {
|
||||||
|
*rgba = 0;
|
||||||
|
}
|
||||||
|
++rgba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump frame to a PAM file. Returns true on success.
|
||||||
|
static int DumpFrame(const char filename[], const char dump_folder[],
|
||||||
|
uint32_t frame_num, const uint8_t rgba[],
|
||||||
|
int canvas_width, int canvas_height) {
|
||||||
|
int ok = 0;
|
||||||
|
size_t max_len;
|
||||||
|
int y;
|
||||||
|
const char* base_name = NULL;
|
||||||
|
char* file_name = NULL;
|
||||||
|
FILE* f = NULL;
|
||||||
|
const char* row;
|
||||||
|
|
||||||
|
base_name = strrchr(filename, '/');
|
||||||
|
base_name = (base_name == NULL) ? filename : base_name + 1;
|
||||||
|
max_len = strlen(dump_folder) + 1 + strlen(base_name)
|
||||||
|
+ strlen("_frame_") + strlen(".pam") + 8;
|
||||||
|
file_name = (char*)malloc(max_len * sizeof(*file_name));
|
||||||
|
if (file_name == NULL) goto End;
|
||||||
|
|
||||||
|
if (snprintf(file_name, max_len, "%s/%s_frame_%d.pam",
|
||||||
|
dump_folder, base_name, frame_num) < 0) {
|
||||||
|
fprintf(stderr, "Error while generating file name\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(file_name, "wb");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "Error opening file for writing: %s\n", file_name);
|
||||||
|
ok = 0;
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
|
||||||
|
"DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
|
||||||
|
canvas_width, canvas_height) < 0) {
|
||||||
|
fprintf(stderr, "Write error for file %s\n", file_name);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
row = (const char*)rgba;
|
||||||
|
for (y = 0; y < canvas_height; ++y) {
|
||||||
|
if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) {
|
||||||
|
fprintf(stderr, "Error writing to file: %s\n", file_name);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
row += canvas_width * kNumChannels;
|
||||||
|
}
|
||||||
|
ok = 1;
|
||||||
|
End:
|
||||||
|
if (f != NULL) fclose(f);
|
||||||
|
free(file_name);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// WebP Decoding.
|
||||||
|
|
||||||
|
// Returns true if this is a valid WebP bitstream.
|
||||||
|
static int IsWebP(const WebPData* const webp_data) {
|
||||||
|
return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read animated WebP bitstream 'file_str' into 'AnimatedImage' struct.
|
||||||
|
static int ReadAnimatedWebP(const char filename[],
|
||||||
|
const WebPData* const webp_data,
|
||||||
|
AnimatedImage* const image, int dump_frames,
|
||||||
|
const char dump_folder[]) {
|
||||||
|
int ok = 0;
|
||||||
|
int dump_ok = 1;
|
||||||
|
uint32_t frame_index = 0;
|
||||||
|
int prev_frame_timestamp = 0;
|
||||||
|
WebPAnimDecoder* dec;
|
||||||
|
WebPAnimInfo anim_info;
|
||||||
|
|
||||||
|
memset(image, 0, sizeof(*image));
|
||||||
|
|
||||||
|
dec = WebPAnimDecoderNew(webp_data, NULL);
|
||||||
|
if (dec == NULL) {
|
||||||
|
fprintf(stderr, "Error parsing image: %s\n", filename);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WebPAnimDecoderGetInfo(dec, &anim_info)) {
|
||||||
|
fprintf(stderr, "Error getting global info about the animation\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation properties.
|
||||||
|
image->canvas_width = anim_info.canvas_width;
|
||||||
|
image->canvas_height = anim_info.canvas_height;
|
||||||
|
image->loop_count = anim_info.loop_count;
|
||||||
|
image->bgcolor = anim_info.bgcolor;
|
||||||
|
|
||||||
|
// Allocate frames.
|
||||||
|
if (!AllocateFrames(image, anim_info.frame_count)) return 0;
|
||||||
|
|
||||||
|
// Decode frames.
|
||||||
|
while (WebPAnimDecoderHasMoreFrames(dec)) {
|
||||||
|
DecodedFrame* curr_frame;
|
||||||
|
uint8_t* curr_rgba;
|
||||||
|
uint8_t* frame_rgba;
|
||||||
|
int timestamp;
|
||||||
|
|
||||||
|
if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) {
|
||||||
|
fprintf(stderr, "Error decoding frame #%u\n", frame_index);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
assert(frame_index < anim_info.frame_count);
|
||||||
|
curr_frame = &image->frames[frame_index];
|
||||||
|
curr_rgba = curr_frame->rgba;
|
||||||
|
curr_frame->duration = timestamp - prev_frame_timestamp;
|
||||||
|
curr_frame->is_key_frame = 0; // Unused.
|
||||||
|
memcpy(curr_rgba, frame_rgba,
|
||||||
|
image->canvas_width * kNumChannels * image->canvas_height);
|
||||||
|
|
||||||
|
// Needed only because we may want to compare with GIF later.
|
||||||
|
CleanupTransparentPixels((uint32_t*)curr_rgba,
|
||||||
|
image->canvas_width, image->canvas_height);
|
||||||
|
|
||||||
|
if (dump_frames && dump_ok) {
|
||||||
|
dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
|
||||||
|
image->canvas_width, image->canvas_height);
|
||||||
|
if (!dump_ok) { // Print error once, but continue decode loop.
|
||||||
|
fprintf(stderr, "Error dumping frames to %s\n", dump_folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++frame_index;
|
||||||
|
prev_frame_timestamp = timestamp;
|
||||||
|
}
|
||||||
|
ok = dump_ok;
|
||||||
|
|
||||||
|
End:
|
||||||
|
WebPAnimDecoderDelete(dec);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// GIF Decoding.
|
||||||
|
|
||||||
|
#ifdef WEBP_HAVE_GIF
|
||||||
|
|
||||||
|
// Returns true if this is a valid GIF bitstream.
|
||||||
|
static int IsGIF(const WebPData* const data) {
|
||||||
|
return data->size > GIF_STAMP_LEN &&
|
||||||
|
(!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) ||
|
||||||
|
!memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) ||
|
||||||
|
!memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||||
|
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||||
|
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||||
|
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||||
|
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||||
|
#else
|
||||||
|
# define LOCAL_GIF_VERSION 0
|
||||||
|
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !LOCAL_GIF_PREREQ(5, 0)
|
||||||
|
|
||||||
|
// Added in v5.0
|
||||||
|
typedef struct {
|
||||||
|
int DisposalMode;
|
||||||
|
#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
|
||||||
|
#define DISPOSE_DO_NOT 1 // Leave image in place
|
||||||
|
#define DISPOSE_BACKGROUND 2 // Set area to background color
|
||||||
|
#define DISPOSE_PREVIOUS 3 // Restore to previous content
|
||||||
|
int UserInputFlag; // User confirmation required before disposal
|
||||||
|
int DelayTime; // Pre-display delay in 0.01sec units
|
||||||
|
int TransparentColor; // Palette index for transparency, -1 if none
|
||||||
|
#define NO_TRANSPARENT_COLOR -1
|
||||||
|
} GraphicsControlBlock;
|
||||||
|
|
||||||
|
static int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||||
|
const GifByteType* GifExtension,
|
||||||
|
GraphicsControlBlock* gcb) {
|
||||||
|
if (GifExtensionLength != 4) {
|
||||||
|
return GIF_ERROR;
|
||||||
|
}
|
||||||
|
gcb->DisposalMode = (GifExtension[0] >> 2) & 0x07;
|
||||||
|
gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0;
|
||||||
|
gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8);
|
||||||
|
if (GifExtension[0] & 0x01) {
|
||||||
|
gcb->TransparentColor = (int)GifExtension[3];
|
||||||
|
} else {
|
||||||
|
gcb->TransparentColor = NO_TRANSPARENT_COLOR;
|
||||||
|
}
|
||||||
|
return GIF_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
|
||||||
|
GraphicsControlBlock* gcb) {
|
||||||
|
int i;
|
||||||
|
if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
|
||||||
|
return GIF_ERROR;
|
||||||
|
}
|
||||||
|
gcb->DisposalMode = DISPOSAL_UNSPECIFIED;
|
||||||
|
gcb->UserInputFlag = 0;
|
||||||
|
gcb->DelayTime = 0;
|
||||||
|
gcb->TransparentColor = NO_TRANSPARENT_COLOR;
|
||||||
|
|
||||||
|
for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
|
||||||
|
ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
|
||||||
|
if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
|
||||||
|
return DGifExtensionToGCB(
|
||||||
|
ep->ByteCount, (const GifByteType*)ep->Bytes, gcb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GIF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONTINUE_EXT_FUNC_CODE 0x00
|
||||||
|
|
||||||
|
// Signature was changed in v5.0
|
||||||
|
#define DGifOpenFileName(a, b) DGifOpenFileName(a)
|
||||||
|
|
||||||
|
#endif // !LOCAL_GIF_PREREQ(5, 0)
|
||||||
|
|
||||||
|
// Signature changed in v5.1
|
||||||
|
#if !LOCAL_GIF_PREREQ(5, 1)
|
||||||
|
#define DGifCloseFile(a, b) DGifCloseFile(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||||
|
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
|
||||||
|
#if LOCAL_GIF_PREREQ(4, 2)
|
||||||
|
#if LOCAL_GIF_PREREQ(5, 0)
|
||||||
|
const char* error_str =
|
||||||
|
GifErrorString((gif == NULL) ? gif_error : gif->Error);
|
||||||
|
#else
|
||||||
|
const char* error_str = GifErrorString();
|
||||||
|
(void)gif;
|
||||||
|
#endif
|
||||||
|
if (error_str == NULL) error_str = "Unknown error";
|
||||||
|
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
|
||||||
|
#else
|
||||||
|
(void)gif;
|
||||||
|
fprintf(stderr, "GIFLib Error %d: ", gif_error);
|
||||||
|
PrintGifError();
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
|
||||||
|
const DecodedFrame* const prev_frame,
|
||||||
|
int canvas_width, int canvas_height) {
|
||||||
|
if (prev_frame == NULL) return 1;
|
||||||
|
if (prev_dispose == DISPOSE_BACKGROUND) {
|
||||||
|
if (IsFullFrame(prev_desc->Width, prev_desc->Height,
|
||||||
|
canvas_width, canvas_height)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (prev_frame->is_key_frame) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetTransparentIndexGIF(GifFileType* gif) {
|
||||||
|
GraphicsControlBlock first_gcb;
|
||||||
|
memset(&first_gcb, 0, sizeof(first_gcb));
|
||||||
|
DGifSavedExtensionToGCB(gif, 0, &first_gcb);
|
||||||
|
return first_gcb.TransparentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
|
||||||
|
const int transparent_index = GetTransparentIndexGIF(gif);
|
||||||
|
const ColorMapObject* const color_map = gif->SColorMap;
|
||||||
|
if (transparent_index != NO_TRANSPARENT_COLOR &&
|
||||||
|
gif->SBackGroundColor == transparent_index) {
|
||||||
|
return 0x00000000; // Special case: transparent black.
|
||||||
|
} else if (color_map == NULL || color_map->Colors == NULL
|
||||||
|
|| gif->SBackGroundColor >= color_map->ColorCount) {
|
||||||
|
return 0xffffffff; // Invalid: assume white.
|
||||||
|
} else {
|
||||||
|
const GifColorType color = color_map->Colors[gif->SBackGroundColor];
|
||||||
|
return (0xff << 24) |
|
||||||
|
(color.Red << 16) |
|
||||||
|
(color.Green << 8) |
|
||||||
|
(color.Blue << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find appropriate app extension and get loop count from the next extension.
|
||||||
|
static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < gif->ImageCount; ++i) {
|
||||||
|
const SavedImage* const image = &gif->SavedImages[i];
|
||||||
|
int j;
|
||||||
|
for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) {
|
||||||
|
const ExtensionBlock* const eb1 = image->ExtensionBlocks + j;
|
||||||
|
const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1;
|
||||||
|
const char* const signature = (const char*)eb1->Bytes;
|
||||||
|
const int signature_is_ok =
|
||||||
|
(eb1->Function == APPLICATION_EXT_FUNC_CODE) &&
|
||||||
|
(eb1->ByteCount == 11) &&
|
||||||
|
(!memcmp(signature, "NETSCAPE2.0", 11) ||
|
||||||
|
!memcmp(signature, "ANIMEXTS1.0", 11));
|
||||||
|
if (signature_is_ok &&
|
||||||
|
eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
|
||||||
|
eb2->Bytes[0] == 1) {
|
||||||
|
return ((uint32_t)(eb2->Bytes[2]) << 8) +
|
||||||
|
((uint32_t)(eb2->Bytes[1]) << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; // Default.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get duration of 'n'th frame in milliseconds.
|
||||||
|
static int GetFrameDurationGIF(GifFileType* gif, int n) {
|
||||||
|
GraphicsControlBlock gcb;
|
||||||
|
memset(&gcb, 0, sizeof(gcb));
|
||||||
|
DGifSavedExtensionToGCB(gif, n, &gcb);
|
||||||
|
return gcb.DelayTime * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if frame 'target' completely covers 'covered'.
|
||||||
|
static int CoversFrameGIF(const GifImageDesc* const target,
|
||||||
|
const GifImageDesc* const covered) {
|
||||||
|
return target->Left <= covered->Left &&
|
||||||
|
covered->Left + covered->Width <= target->Left + target->Width &&
|
||||||
|
target->Top <= covered->Top &&
|
||||||
|
covered->Top + covered->Height <= target->Top + target->Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RemapPixelsGIF(const uint8_t* const src,
|
||||||
|
const ColorMapObject* const cmap,
|
||||||
|
int transparent_color, int len, uint8_t* dst) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
if (src[i] != transparent_color) {
|
||||||
|
// If a pixel in the current frame is transparent, we don't modify it, so
|
||||||
|
// that we can see-through the corresponding pixel from an earlier frame.
|
||||||
|
const GifColorType c = cmap->Colors[src[i]];
|
||||||
|
dst[4 * i + 0] = c.Red;
|
||||||
|
dst[4 * i + 1] = c.Green;
|
||||||
|
dst[4 * i + 2] = c.Blue;
|
||||||
|
dst[4 * i + 3] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadFrameGIF(const SavedImage* const gif_image,
|
||||||
|
const ColorMapObject* cmap, int transparent_color,
|
||||||
|
int out_stride, uint8_t* const dst) {
|
||||||
|
const GifImageDesc* image_desc = &gif_image->ImageDesc;
|
||||||
|
const uint8_t* in;
|
||||||
|
uint8_t* out;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (image_desc->ColorMap) cmap = image_desc->ColorMap;
|
||||||
|
|
||||||
|
if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
|
||||||
|
fprintf(stderr, "Potentially corrupt color map.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
in = (const uint8_t*)gif_image->RasterBits;
|
||||||
|
out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels;
|
||||||
|
|
||||||
|
for (j = 0; j < image_desc->Height; ++j) {
|
||||||
|
RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out);
|
||||||
|
in += image_desc->Width;
|
||||||
|
out += out_stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
|
||||||
|
static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||||
|
int dump_frames, const char dump_folder[]) {
|
||||||
|
uint32_t frame_count;
|
||||||
|
uint32_t canvas_width, canvas_height;
|
||||||
|
uint32_t i;
|
||||||
|
int gif_error;
|
||||||
|
GifFileType* gif;
|
||||||
|
|
||||||
|
gif = DGifOpenFileName(filename, NULL);
|
||||||
|
if (gif == NULL) {
|
||||||
|
fprintf(stderr, "Could not read file: %s.\n", filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gif_error = DGifSlurp(gif);
|
||||||
|
if (gif_error != GIF_OK) {
|
||||||
|
fprintf(stderr, "Could not parse image: %s.\n", filename);
|
||||||
|
GIFDisplayError(gif, gif_error);
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation properties.
|
||||||
|
image->canvas_width = (uint32_t)gif->SWidth;
|
||||||
|
image->canvas_height = (uint32_t)gif->SHeight;
|
||||||
|
if (image->canvas_width > MAX_CANVAS_SIZE ||
|
||||||
|
image->canvas_height > MAX_CANVAS_SIZE) {
|
||||||
|
fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
|
||||||
|
image->canvas_width, image->canvas_height);
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
image->loop_count = GetLoopCountGIF(gif);
|
||||||
|
image->bgcolor = GetBackgroundColorGIF(gif);
|
||||||
|
|
||||||
|
frame_count = (uint32_t)gif->ImageCount;
|
||||||
|
if (frame_count == 0) {
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image->canvas_width == 0 || image->canvas_height == 0) {
|
||||||
|
image->canvas_width = gif->SavedImages[0].ImageDesc.Width;
|
||||||
|
image->canvas_height = gif->SavedImages[0].ImageDesc.Height;
|
||||||
|
gif->SavedImages[0].ImageDesc.Left = 0;
|
||||||
|
gif->SavedImages[0].ImageDesc.Top = 0;
|
||||||
|
if (image->canvas_width == 0 || image->canvas_height == 0) {
|
||||||
|
fprintf(stderr, "Invalid canvas size in GIF.\n");
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Allocate frames.
|
||||||
|
AllocateFrames(image, frame_count);
|
||||||
|
|
||||||
|
canvas_width = image->canvas_width;
|
||||||
|
canvas_height = image->canvas_height;
|
||||||
|
|
||||||
|
// Decode and reconstruct frames.
|
||||||
|
for (i = 0; i < frame_count; ++i) {
|
||||||
|
const int canvas_width_in_bytes = canvas_width * kNumChannels;
|
||||||
|
const SavedImage* const curr_gif_image = &gif->SavedImages[i];
|
||||||
|
GraphicsControlBlock curr_gcb;
|
||||||
|
DecodedFrame* curr_frame;
|
||||||
|
uint8_t* curr_rgba;
|
||||||
|
|
||||||
|
memset(&curr_gcb, 0, sizeof(curr_gcb));
|
||||||
|
DGifSavedExtensionToGCB(gif, i, &curr_gcb);
|
||||||
|
|
||||||
|
curr_frame = &image->frames[i];
|
||||||
|
curr_rgba = curr_frame->rgba;
|
||||||
|
curr_frame->duration = GetFrameDurationGIF(gif, i);
|
||||||
|
|
||||||
|
if (i == 0) { // Initialize as transparent.
|
||||||
|
curr_frame->is_key_frame = 1;
|
||||||
|
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
|
||||||
|
} else {
|
||||||
|
DecodedFrame* const prev_frame = &image->frames[i - 1];
|
||||||
|
const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
|
||||||
|
GraphicsControlBlock prev_gcb;
|
||||||
|
memset(&prev_gcb, 0, sizeof(prev_gcb));
|
||||||
|
DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb);
|
||||||
|
|
||||||
|
curr_frame->is_key_frame =
|
||||||
|
IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame,
|
||||||
|
canvas_width, canvas_height);
|
||||||
|
|
||||||
|
if (curr_frame->is_key_frame) { // Initialize as transparent.
|
||||||
|
ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
|
||||||
|
} else {
|
||||||
|
int prev_frame_disposed, curr_frame_opaque;
|
||||||
|
int prev_frame_completely_covered;
|
||||||
|
// Initialize with previous canvas.
|
||||||
|
uint8_t* const prev_rgba = image->frames[i - 1].rgba;
|
||||||
|
CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
|
||||||
|
|
||||||
|
// Dispose previous frame rectangle.
|
||||||
|
prev_frame_disposed =
|
||||||
|
(prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
|
||||||
|
prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
|
||||||
|
curr_frame_opaque =
|
||||||
|
(curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
|
||||||
|
prev_frame_completely_covered =
|
||||||
|
curr_frame_opaque &&
|
||||||
|
CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
|
||||||
|
|
||||||
|
if (prev_frame_disposed && !prev_frame_completely_covered) {
|
||||||
|
switch (prev_gcb.DisposalMode) {
|
||||||
|
case DISPOSE_BACKGROUND: {
|
||||||
|
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||||
|
prev_desc->Left, prev_desc->Top,
|
||||||
|
prev_desc->Width, prev_desc->Height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DISPOSE_PREVIOUS: {
|
||||||
|
int src_frame_num = i - 2;
|
||||||
|
while (src_frame_num >= 0) {
|
||||||
|
GraphicsControlBlock src_frame_gcb;
|
||||||
|
memset(&src_frame_gcb, 0, sizeof(src_frame_gcb));
|
||||||
|
DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb);
|
||||||
|
if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break;
|
||||||
|
--src_frame_num;
|
||||||
|
}
|
||||||
|
if (src_frame_num >= 0) {
|
||||||
|
// Restore pixels inside previous frame rectangle to
|
||||||
|
// corresponding pixels in source canvas.
|
||||||
|
uint8_t* const src_frame_rgba =
|
||||||
|
image->frames[src_frame_num].rgba;
|
||||||
|
CopyFrameRectangle(src_frame_rgba, curr_rgba,
|
||||||
|
canvas_width_in_bytes,
|
||||||
|
prev_desc->Left, prev_desc->Top,
|
||||||
|
prev_desc->Width, prev_desc->Height);
|
||||||
|
} else {
|
||||||
|
// Source canvas doesn't exist. So clear previous frame
|
||||||
|
// rectangle to background.
|
||||||
|
ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
|
||||||
|
prev_desc->Left, prev_desc->Top,
|
||||||
|
prev_desc->Width, prev_desc->Height);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break; // Nothing to do.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode current frame.
|
||||||
|
if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor,
|
||||||
|
canvas_width_in_bytes, curr_rgba)) {
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dump_frames) {
|
||||||
|
if (!DumpFrame(filename, dump_folder, i, curr_rgba,
|
||||||
|
canvas_width, canvas_height)) {
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DGifCloseFile(gif, NULL);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int IsGIF(const WebPData* const data) {
|
||||||
|
(void)data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
|
||||||
|
int dump_frames, const char dump_folder[]) {
|
||||||
|
(void)filename;
|
||||||
|
(void)image;
|
||||||
|
(void)dump_frames;
|
||||||
|
(void)dump_folder;
|
||||||
|
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||||
|
"package before building.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WEBP_HAVE_GIF
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||||
|
int dump_frames, const char dump_folder[]) {
|
||||||
|
int ok = 0;
|
||||||
|
WebPData webp_data;
|
||||||
|
|
||||||
|
WebPDataInit(&webp_data);
|
||||||
|
memset(image, 0, sizeof(*image));
|
||||||
|
|
||||||
|
if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) {
|
||||||
|
fprintf(stderr, "Error reading file: %s\n", filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsWebP(&webp_data)) {
|
||||||
|
ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
|
||||||
|
dump_folder);
|
||||||
|
} else if (IsGIF(&webp_data)) {
|
||||||
|
ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Unknown file type: %s. Supported file types are WebP and GIF\n",
|
||||||
|
filename);
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
if (!ok) ClearAnimatedImage(image);
|
||||||
|
WebPDataClear(&webp_data);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Accumulate(double v1, double v2, double* const max_diff,
|
||||||
|
double* const sse) {
|
||||||
|
const double diff = fabs(v1 - v2);
|
||||||
|
if (diff > *max_diff) *max_diff = diff;
|
||||||
|
*sse += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||||
|
uint32_t width, uint32_t height, int premultiply,
|
||||||
|
int* const max_diff, double* const psnr) {
|
||||||
|
const uint32_t stride = width * kNumChannels;
|
||||||
|
const int kAlphaChannel = kNumChannels - 1;
|
||||||
|
double f_max_diff = 0.;
|
||||||
|
double sse = 0.;
|
||||||
|
uint32_t x, y;
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
for (x = 0; x < stride; x += kNumChannels) {
|
||||||
|
int k;
|
||||||
|
const size_t offset = (size_t)y * stride + x;
|
||||||
|
const int alpha1 = rgba1[offset + kAlphaChannel];
|
||||||
|
const int alpha2 = rgba2[offset + kAlphaChannel];
|
||||||
|
Accumulate(alpha1, alpha2, &f_max_diff, &sse);
|
||||||
|
if (!premultiply) {
|
||||||
|
for (k = 0; k < kAlphaChannel; ++k) {
|
||||||
|
Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// premultiply R/G/B channels with alpha value
|
||||||
|
for (k = 0; k < kAlphaChannel; ++k) {
|
||||||
|
Accumulate(rgba1[offset + k] * alpha1 / 255.,
|
||||||
|
rgba2[offset + k] * alpha2 / 255.,
|
||||||
|
&f_max_diff, &sse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*max_diff = (int)f_max_diff;
|
||||||
|
if (*max_diff == 0) {
|
||||||
|
*psnr = 99.; // PSNR when images are identical.
|
||||||
|
} else {
|
||||||
|
sse /= stride * height;
|
||||||
|
*psnr = 4.3429448 * log(255. * 255. / sse);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
examples/anim_util.h
Normal file
63
examples/anim_util.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utilities for animated images
|
||||||
|
|
||||||
|
#ifndef WEBP_EXAMPLES_ANIM_UTIL_H_
|
||||||
|
#define WEBP_EXAMPLES_ANIM_UTIL_H_
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t* rgba; // Decoded and reconstructed full frame.
|
||||||
|
int duration; // Frame duration in milliseconds.
|
||||||
|
int is_key_frame; // True if this frame is a key-frame.
|
||||||
|
} DecodedFrame;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t canvas_width;
|
||||||
|
uint32_t canvas_height;
|
||||||
|
uint32_t bgcolor;
|
||||||
|
uint32_t loop_count;
|
||||||
|
DecodedFrame* frames;
|
||||||
|
uint32_t num_frames;
|
||||||
|
void* raw_mem;
|
||||||
|
} AnimatedImage;
|
||||||
|
|
||||||
|
// Deallocate everything in 'image' (but not the object itself).
|
||||||
|
void ClearAnimatedImage(AnimatedImage* const image);
|
||||||
|
|
||||||
|
// Read animated image file into 'AnimatedImage' struct.
|
||||||
|
// If 'dump_frames' is true, dump frames to 'dump_folder'.
|
||||||
|
// Previous content of 'image' is obliterated.
|
||||||
|
// Upon successful return, content of 'image' must be deleted by
|
||||||
|
// calling 'ClearAnimatedImage'.
|
||||||
|
int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
|
||||||
|
int dump_frames, const char dump_folder[]);
|
||||||
|
|
||||||
|
// Given two RGBA buffers, calculate max pixel difference and PSNR.
|
||||||
|
// If 'premultiply' is true, R/G/B values will be pre-multiplied by the
|
||||||
|
// transparency before comparison.
|
||||||
|
void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
|
||||||
|
uint32_t width, uint32_t height, int premultiply,
|
||||||
|
int* const max_diff, double* const psnr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_EXAMPLES_ANIM_UTIL_H_
|
||||||
334
examples/cwebp.c
334
examples/cwebp.c
@@ -20,16 +20,11 @@
|
|||||||
#include "webp/config.h"
|
#include "webp/config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "webp/encode.h"
|
#include "../examples/example_util.h"
|
||||||
|
#include "../imageio/image_dec.h"
|
||||||
#include "./metadata.h"
|
#include "../imageio/imageio_util.h"
|
||||||
#include "./stopwatch.h"
|
#include "./stopwatch.h"
|
||||||
|
#include "webp/encode.h"
|
||||||
#include "./jpegdec.h"
|
|
||||||
#include "./pngdec.h"
|
|
||||||
#include "./tiffdec.h"
|
|
||||||
#include "./webpdec.h"
|
|
||||||
#include "./wicdec.h"
|
|
||||||
|
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -47,127 +42,81 @@ extern void* VP8GetCPUInfo; // opaque forward declaration.
|
|||||||
|
|
||||||
static int verbose = 0;
|
static int verbose = 0;
|
||||||
|
|
||||||
static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
|
static int ReadYUV(const uint8_t* const data, size_t data_size,
|
||||||
|
WebPPicture* const pic) {
|
||||||
const int use_argb = pic->use_argb;
|
const int use_argb = pic->use_argb;
|
||||||
const int uv_width = (pic->width + 1) / 2;
|
const int uv_width = (pic->width + 1) / 2;
|
||||||
const int uv_height = (pic->height + 1) / 2;
|
const int uv_height = (pic->height + 1) / 2;
|
||||||
int y;
|
const int y_plane_size = pic->width * pic->height;
|
||||||
int ok = 0;
|
const int uv_plane_size = uv_width * uv_height;
|
||||||
|
const size_t expected_data_size = y_plane_size + 2 * uv_plane_size;
|
||||||
|
|
||||||
|
if (data_size != expected_data_size) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"input data doesn't have the expected size (%d instead of %d)\n",
|
||||||
|
(int)data_size, (int)expected_data_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pic->use_argb = 0;
|
pic->use_argb = 0;
|
||||||
if (!WebPPictureAlloc(pic)) return ok;
|
if (!WebPPictureAlloc(pic)) return 0;
|
||||||
|
ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
|
||||||
for (y = 0; y < pic->height; ++y) {
|
pic->width, pic->height);
|
||||||
if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
|
ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
|
||||||
goto End;
|
pic->u, pic->uv_stride, uv_width, uv_height);
|
||||||
}
|
ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
|
||||||
}
|
pic->v, pic->uv_stride, uv_width, uv_height);
|
||||||
for (y = 0; y < uv_height; ++y) {
|
return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
|
||||||
if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
for (y = 0; y < uv_height; ++y) {
|
|
||||||
if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
ok = 1;
|
|
||||||
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
|
|
||||||
|
|
||||||
End:
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
|
||||||
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||||
int keep_alpha, Metadata* const metadata) {
|
int keep_alpha, Metadata* const metadata) {
|
||||||
int ok;
|
int ok = 0;
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
if (pic->width != 0 && pic->height != 0) {
|
if (pic->width != 0 && pic->height != 0) {
|
||||||
// If image size is specified, infer it as YUV format.
|
ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
FILE* in_file = fopen(filename, "rb");
|
ok = ok && ReadYUV(data, data_size, pic);
|
||||||
if (in_file == NULL) {
|
|
||||||
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ok = ReadYUV(in_file, pic);
|
|
||||||
fclose(in_file);
|
|
||||||
} else {
|
} else {
|
||||||
// If no size specified, try to decode it using WIC.
|
// If no size specified, try to decode it using WIC.
|
||||||
ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
|
ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
ok = ReadWebP(filename, pic, keep_alpha, metadata);
|
ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
|
ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||||
}
|
}
|
||||||
|
free((void*)data);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !HAVE_WINCODEC_H
|
#else // !HAVE_WINCODEC_H
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PNG_ = 0,
|
|
||||||
JPEG_,
|
|
||||||
TIFF_, // 'TIFF' clashes with libtiff
|
|
||||||
WEBP_,
|
|
||||||
UNSUPPORTED
|
|
||||||
} InputFileFormat;
|
|
||||||
|
|
||||||
static InputFileFormat GetImageType(FILE* in_file) {
|
|
||||||
InputFileFormat format = UNSUPPORTED;
|
|
||||||
uint32_t magic1, magic2;
|
|
||||||
uint8_t buf[12];
|
|
||||||
|
|
||||||
if ((fread(&buf[0], 12, 1, in_file) != 1) ||
|
|
||||||
(fseek(in_file, 0, SEEK_SET) != 0)) {
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
magic1 = ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
|
||||||
magic2 = ((uint32_t)buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
|
|
||||||
if (magic1 == 0x89504E47U) {
|
|
||||||
format = PNG_;
|
|
||||||
} else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
|
|
||||||
format = JPEG_;
|
|
||||||
} else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
|
|
||||||
format = TIFF_;
|
|
||||||
} else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
|
|
||||||
format = WEBP_;
|
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
static int ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||||
int keep_alpha, Metadata* const metadata) {
|
int keep_alpha, Metadata* const metadata) {
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
FILE* in_file = fopen(filename, "rb");
|
|
||||||
if (in_file == NULL) {
|
ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
if (!ok) goto End;
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pic->width == 0 || pic->height == 0) {
|
if (pic->width == 0 || pic->height == 0) {
|
||||||
// If no size specified, try to decode it as PNG/JPEG (as appropriate).
|
WebPImageReader reader = WebPGuessImageReader(data, data_size);
|
||||||
const InputFileFormat format = GetImageType(in_file);
|
ok = reader(data, data_size, pic, keep_alpha, metadata);
|
||||||
if (format == PNG_) {
|
|
||||||
ok = ReadPNG(in_file, pic, keep_alpha, metadata);
|
|
||||||
} else if (format == JPEG_) {
|
|
||||||
ok = ReadJPEG(in_file, pic, metadata);
|
|
||||||
} else if (format == TIFF_) {
|
|
||||||
ok = ReadTIFF(filename, pic, keep_alpha, metadata);
|
|
||||||
} else if (format == WEBP_) {
|
|
||||||
ok = ReadWebP(filename, pic, keep_alpha, metadata);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// If image size is specified, infer it as YUV format.
|
// If image size is specified, infer it as YUV format.
|
||||||
ok = ReadYUV(in_file, pic);
|
ok = ReadYUV(data, data_size, pic);
|
||||||
}
|
}
|
||||||
|
End:
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||||
}
|
}
|
||||||
|
free((void*)data);
|
||||||
fclose(in_file);
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +160,8 @@ static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
|
|||||||
const char* const description) {
|
const char* const description) {
|
||||||
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
|
||||||
description, stats->lossless_size);
|
description, stats->lossless_size);
|
||||||
|
fprintf(stderr, " * Header size: %d bytes, image data size: %d\n",
|
||||||
|
stats->lossless_hdr_size, stats->lossless_data_size);
|
||||||
if (stats->lossless_features) {
|
if (stats->lossless_features) {
|
||||||
fprintf(stderr, " * Lossless features used:");
|
fprintf(stderr, " * Lossless features used:");
|
||||||
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
|
if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
|
||||||
@@ -346,6 +297,10 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
|||||||
const int uv_width = (picture->width + 1) / 2;
|
const int uv_width = (picture->width + 1) / 2;
|
||||||
const int uv_height = (picture->height + 1) / 2;
|
const int uv_height = (picture->height + 1) / 2;
|
||||||
const int stride = (picture->width + 1) & ~1;
|
const int stride = (picture->width + 1) & ~1;
|
||||||
|
const uint8_t* src_y = picture->y;
|
||||||
|
const uint8_t* src_u = picture->u;
|
||||||
|
const uint8_t* src_v = picture->v;
|
||||||
|
const uint8_t* src_a = picture->a;
|
||||||
const int alpha_height =
|
const int alpha_height =
|
||||||
WebPPictureHasTransparency(picture) ? picture->height : 0;
|
WebPPictureHasTransparency(picture) ? picture->height : 0;
|
||||||
const int height = picture->height + uv_height + alpha_height;
|
const int height = picture->height + uv_height + alpha_height;
|
||||||
@@ -353,20 +308,20 @@ static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
|
|||||||
if (f == NULL) return 0;
|
if (f == NULL) return 0;
|
||||||
fprintf(f, "P5\n%d %d\n255\n", stride, height);
|
fprintf(f, "P5\n%d %d\n255\n", stride, height);
|
||||||
for (y = 0; y < picture->height; ++y) {
|
for (y = 0; y < picture->height; ++y) {
|
||||||
if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1)
|
if (fwrite(src_y, picture->width, 1, f) != 1) return 0;
|
||||||
return 0;
|
|
||||||
if (picture->width & 1) fputc(0, f); // pad
|
if (picture->width & 1) fputc(0, f); // pad
|
||||||
|
src_y += picture->y_stride;
|
||||||
}
|
}
|
||||||
for (y = 0; y < uv_height; ++y) {
|
for (y = 0; y < uv_height; ++y) {
|
||||||
if (fwrite(picture->u + y * picture->uv_stride, uv_width, 1, f) != 1)
|
if (fwrite(src_u, uv_width, 1, f) != 1) return 0;
|
||||||
return 0;
|
if (fwrite(src_v, uv_width, 1, f) != 1) return 0;
|
||||||
if (fwrite(picture->v + y * picture->uv_stride, uv_width, 1, f) != 1)
|
src_u += picture->uv_stride;
|
||||||
return 0;
|
src_v += picture->uv_stride;
|
||||||
}
|
}
|
||||||
for (y = 0; y < alpha_height; ++y) {
|
for (y = 0; y < alpha_height; ++y) {
|
||||||
if (fwrite(picture->a + y * picture->a_stride, picture->width, 1, f) != 1)
|
if (fwrite(src_a, picture->width, 1, f) != 1) return 0;
|
||||||
return 0;
|
|
||||||
if (picture->width & 1) fputc(0, f); // pad
|
if (picture->width & 1) fputc(0, f); // pad
|
||||||
|
src_a += picture->a_stride;
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -564,33 +519,38 @@ static void HelpLong(void) {
|
|||||||
printf("Windows builds can take as input any of the files handled by WIC.\n");
|
printf("Windows builds can take as input any of the files handled by WIC.\n");
|
||||||
#endif
|
#endif
|
||||||
printf("\nOptions:\n");
|
printf("\nOptions:\n");
|
||||||
printf(" -h / -help ............ short help\n");
|
printf(" -h / -help ............. short help\n");
|
||||||
printf(" -H / -longhelp ........ long help\n");
|
printf(" -H / -longhelp ......... long help\n");
|
||||||
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
|
printf(" -q <float> ............. quality factor (0:small..100:big), "
|
||||||
printf(" -alpha_q <int> ......... transparency-compression quality "
|
"default=75\n");
|
||||||
"(0..100)\n");
|
printf(" -alpha_q <int> ......... transparency-compression quality (0..100),"
|
||||||
|
"\n default=100\n");
|
||||||
printf(" -preset <string> ....... preset setting, one of:\n");
|
printf(" -preset <string> ....... preset setting, one of:\n");
|
||||||
printf(" default, photo, picture,\n");
|
printf(" default, photo, picture,\n");
|
||||||
printf(" drawing, icon, text\n");
|
printf(" drawing, icon, text\n");
|
||||||
printf(" -preset must come first, as it overwrites other parameters\n");
|
printf(" -preset must come first, as it overwrites other parameters\n");
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
printf(" -z <int> ............... activates lossless preset with given\n"
|
printf(" -z <int> ............... activates lossless preset with given\n"
|
||||||
" level in [0:fast, ..., 9:slowest]\n");
|
" level in [0:fast, ..., 9:slowest]\n");
|
||||||
#endif
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
printf(" -m <int> ............... compression method (0=fast, 6=slowest), "
|
||||||
printf(" -segments <int> ........ number of segments to use (1..4)\n");
|
"default=4\n");
|
||||||
|
printf(" -segments <int> ........ number of segments to use (1..4), "
|
||||||
|
"default=4\n");
|
||||||
printf(" -size <int> ............ target size (in bytes)\n");
|
printf(" -size <int> ............ target size (in bytes)\n");
|
||||||
printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
|
printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" -s <int> <int> ......... input size (width x height) for YUV\n");
|
printf(" -s <int> <int> ......... input size (width x height) for YUV\n");
|
||||||
printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max)\n");
|
printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
|
||||||
printf(" -f <int> ............... filter strength (0=off..100)\n");
|
"default=50\n");
|
||||||
|
printf(" -f <int> ............... filter strength (0=off..100), "
|
||||||
|
"default=60\n");
|
||||||
printf(" -sharpness <int> ....... "
|
printf(" -sharpness <int> ....... "
|
||||||
"filter sharpness (0:most .. 7:least sharp)\n");
|
"filter sharpness (0:most .. 7:least sharp), default=0\n");
|
||||||
printf(" -strong ................ use strong filter instead "
|
printf(" -strong ................ use strong filter instead "
|
||||||
"of simple (default)\n");
|
"of simple (default)\n");
|
||||||
printf(" -nostrong .............. use simple filter instead of strong\n");
|
printf(" -nostrong .............. use simple filter instead of strong\n");
|
||||||
|
printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
|
||||||
|
"conversion\n");
|
||||||
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
|
printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
|
||||||
printf(" "
|
printf(" "
|
||||||
"the first partition (0=no degradation ... 100=full)\n");
|
"the first partition (0=no degradation ... 100=full)\n");
|
||||||
@@ -604,16 +564,24 @@ static void HelpLong(void) {
|
|||||||
printf(" -print_ssim ............ prints averaged SSIM distortion\n");
|
printf(" -print_ssim ............ prints averaged SSIM distortion\n");
|
||||||
printf(" -print_lsim ............ prints local-similarity distortion\n");
|
printf(" -print_lsim ............ prints local-similarity distortion\n");
|
||||||
printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n");
|
printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n");
|
||||||
printf(" -alpha_method <int> .... transparency-compression method (0..1)\n");
|
printf(" -alpha_method <int> .... transparency-compression method (0..1), "
|
||||||
|
"default=1\n");
|
||||||
printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n");
|
printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n");
|
||||||
printf(" one of: none, fast (default) or best\n");
|
printf(" one of: none, fast (default) or best\n");
|
||||||
printf(" -alpha_cleanup ......... clean RGB values in transparent area\n");
|
printf(" -exact ................. preserve RGB values in transparent area, "
|
||||||
|
"default=off\n");
|
||||||
printf(" -blend_alpha <hex> ..... blend colors against background color\n"
|
printf(" -blend_alpha <hex> ..... blend colors against background color\n"
|
||||||
" expressed as RGB values written in\n"
|
" expressed as RGB values written in\n"
|
||||||
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
" hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
|
||||||
" green=0xe0 and blue=0xd0\n");
|
" green=0xe0 and blue=0xd0\n");
|
||||||
printf(" -noalpha ............... discard any transparency information\n");
|
printf(" -noalpha ............... discard any transparency information\n");
|
||||||
printf(" -lossless .............. encode image losslessly\n");
|
printf(" -lossless .............. encode image losslessly, default=off\n");
|
||||||
|
printf(" -near_lossless <int> ... use near-lossless image\n"
|
||||||
|
" preprocessing (0..100=off), "
|
||||||
|
"default=100\n");
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES /* not documented yet */
|
||||||
|
printf(" -delta_palette ......... use delta palettization\n");
|
||||||
|
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||||
printf(" -hint <string> ......... specify image characteristics hint,\n");
|
printf(" -hint <string> ......... specify image characteristics hint,\n");
|
||||||
printf(" one of: photo, picture or graph\n");
|
printf(" one of: photo, picture or graph\n");
|
||||||
|
|
||||||
@@ -678,10 +646,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
uint32_t background_color = 0xffffffu;
|
uint32_t background_color = 0xffffffu;
|
||||||
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
|
int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
|
||||||
int resize_w = 0, resize_h = 0;
|
int resize_w = 0, resize_h = 0;
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
int lossless_preset = 6;
|
int lossless_preset = 6;
|
||||||
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
|
int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
|
||||||
#endif
|
|
||||||
int show_progress = 0;
|
int show_progress = 0;
|
||||||
int keep_metadata = 0;
|
int keep_metadata = 0;
|
||||||
int metadata_written = 0;
|
int metadata_written = 0;
|
||||||
@@ -709,6 +675,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (c = 1; c < argc; ++c) {
|
for (c = 1; c < argc; ++c) {
|
||||||
|
int parse_error = 0;
|
||||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||||
HelpShort();
|
HelpShort();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -732,30 +699,37 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else if (!strcmp(argv[c], "-short")) {
|
} else if (!strcmp(argv[c], "-short")) {
|
||||||
++short_output;
|
++short_output;
|
||||||
} else if (!strcmp(argv[c], "-s") && c < argc - 2) {
|
} else if (!strcmp(argv[c], "-s") && c < argc - 2) {
|
||||||
picture.width = strtol(argv[++c], NULL, 0);
|
picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
picture.height = strtol(argv[++c], NULL, 0);
|
picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
|
||||||
|
picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Specified dimension (%d x %d) is out of range.\n",
|
||||||
|
picture.width, picture.height);
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
||||||
config.method = strtol(argv[++c], NULL, 0);
|
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
use_lossless_preset = 0; // disable -z option
|
use_lossless_preset = 0; // disable -z option
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
||||||
config.quality = (float)strtod(argv[++c], NULL);
|
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
use_lossless_preset = 0; // disable -z option
|
use_lossless_preset = 0; // disable -z option
|
||||||
} else if (!strcmp(argv[c], "-z") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-z") && c < argc - 1) {
|
||||||
lossless_preset = strtol(argv[++c], NULL, 0);
|
lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
if (use_lossless_preset != 0) use_lossless_preset = 1;
|
if (use_lossless_preset != 0) use_lossless_preset = 1;
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
|
||||||
config.alpha_quality = strtol(argv[++c], NULL, 0);
|
config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
||||||
config.alpha_compression = strtol(argv[++c], NULL, 0);
|
config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-alpha_cleanup")) {
|
} else if (!strcmp(argv[c], "-alpha_cleanup")) {
|
||||||
keep_alpha = keep_alpha ? 2 : 0;
|
// This flag is obsolete, does opposite of -exact.
|
||||||
|
config.exact = 0;
|
||||||
|
} else if (!strcmp(argv[c], "-exact")) {
|
||||||
|
config.exact = 1;
|
||||||
} else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
|
||||||
blend_alpha = 1;
|
blend_alpha = 1;
|
||||||
background_color = strtol(argv[++c], NULL, 16); // <- parses '0x' prefix
|
// background color is given in hex with an optional '0x' prefix
|
||||||
|
background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
|
||||||
background_color = background_color & 0x00ffffffu;
|
background_color = background_color & 0x00ffffffu;
|
||||||
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
||||||
++c;
|
++c;
|
||||||
@@ -773,6 +747,14 @@ int main(int argc, const char *argv[]) {
|
|||||||
keep_alpha = 0;
|
keep_alpha = 0;
|
||||||
} else if (!strcmp(argv[c], "-lossless")) {
|
} else if (!strcmp(argv[c], "-lossless")) {
|
||||||
config.lossless = 1;
|
config.lossless = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) {
|
||||||
|
config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
config.lossless = 1; // use near-lossless only with lossless
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
} else if (!strcmp(argv[c], "-delta_palette")) {
|
||||||
|
config.use_delta_palette = 1;
|
||||||
|
config.lossless = 1; // delta-palette is for lossless only
|
||||||
|
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||||
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
|
||||||
++c;
|
++c;
|
||||||
if (!strcmp(argv[c], "photo")) {
|
if (!strcmp(argv[c], "photo")) {
|
||||||
@@ -786,13 +768,13 @@ int main(int argc, const char *argv[]) {
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
||||||
config.target_size = strtol(argv[++c], NULL, 0);
|
config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
|
||||||
config.target_PSNR = (float)strtod(argv[++c], NULL);
|
config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
|
||||||
config.sns_strength = strtol(argv[++c], NULL, 0);
|
config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
||||||
config.filter_strength = strtol(argv[++c], NULL, 0);
|
config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-af")) {
|
} else if (!strcmp(argv[c], "-af")) {
|
||||||
config.autofilter = 1;
|
config.autofilter = 1;
|
||||||
} else if (!strcmp(argv[c], "-jpeg_like")) {
|
} else if (!strcmp(argv[c], "-jpeg_like")) {
|
||||||
@@ -806,26 +788,28 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else if (!strcmp(argv[c], "-nostrong")) {
|
} else if (!strcmp(argv[c], "-nostrong")) {
|
||||||
config.filter_type = 0;
|
config.filter_type = 0;
|
||||||
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
||||||
config.filter_sharpness = strtol(argv[++c], NULL, 0);
|
config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-sharp_yuv")) {
|
||||||
|
config.use_sharp_yuv = 1;
|
||||||
} else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
|
||||||
config.pass = strtol(argv[++c], NULL, 0);
|
config.pass = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
|
||||||
config.preprocessing = strtol(argv[++c], NULL, 0);
|
config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
|
||||||
config.segments = strtol(argv[++c], NULL, 0);
|
config.segments = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
|
||||||
config.partition_limit = strtol(argv[++c], NULL, 0);
|
config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-map") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-map") && c < argc - 1) {
|
||||||
picture.extra_info_type = strtol(argv[++c], NULL, 0);
|
picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
||||||
crop = 1;
|
crop = 1;
|
||||||
crop_x = strtol(argv[++c], NULL, 0);
|
crop_x = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
crop_y = strtol(argv[++c], NULL, 0);
|
crop_y = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
crop_w = strtol(argv[++c], NULL, 0);
|
crop_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
crop_h = strtol(argv[++c], NULL, 0);
|
crop_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
|
} else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
|
||||||
resize_w = strtol(argv[++c], NULL, 0);
|
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
resize_h = strtol(argv[++c], NULL, 0);
|
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
} else if (!strcmp(argv[c], "-noasm")) {
|
} else if (!strcmp(argv[c], "-noasm")) {
|
||||||
VP8GetCPUInfo = NULL;
|
VP8GetCPUInfo = NULL;
|
||||||
@@ -920,6 +904,11 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
in_file = argv[c];
|
in_file = argv[c];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_error) {
|
||||||
|
HelpLong();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (in_file == NULL) {
|
if (in_file == NULL) {
|
||||||
fprintf(stderr, "No input file specified!\n");
|
fprintf(stderr, "No input file specified!\n");
|
||||||
@@ -927,14 +916,12 @@ int main(int argc, const char *argv[]) {
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
if (use_lossless_preset == 1) {
|
if (use_lossless_preset == 1) {
|
||||||
if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
|
if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
|
||||||
fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
|
fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check for unsupported command line options for lossless mode and log
|
// Check for unsupported command line options for lossless mode and log
|
||||||
// warning for such options.
|
// warning for such options.
|
||||||
@@ -948,13 +935,23 @@ int main(int argc, const char *argv[]) {
|
|||||||
" encoding. Ignoring this option!\n");
|
" encoding. Ignoring this option!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If a target size or PSNR was given, but somehow the -pass option was
|
||||||
|
// omitted, force a reasonable value.
|
||||||
|
if (config.target_size > 0 || config.target_PSNR > 0) {
|
||||||
|
if (config.pass == 1) config.pass = 6;
|
||||||
|
}
|
||||||
|
|
||||||
if (!WebPValidateConfig(&config)) {
|
if (!WebPValidateConfig(&config)) {
|
||||||
fprintf(stderr, "Error! Invalid configuration.\n");
|
fprintf(stderr, "Error! Invalid configuration.\n");
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the input
|
// Read the input. We need to decide if we prefer ARGB or YUVA
|
||||||
|
// samples, depending on the expected compression mode (this saves
|
||||||
|
// some conversion steps).
|
||||||
|
picture.use_argb = (config.lossless || config.use_sharp_yuv ||
|
||||||
|
config.preprocessing > 0 ||
|
||||||
|
crop || (resize_w | resize_h) > 0);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
StopwatchReset(&stop_watch);
|
StopwatchReset(&stop_watch);
|
||||||
}
|
}
|
||||||
@@ -969,10 +966,6 @@ int main(int argc, const char *argv[]) {
|
|||||||
WebPBlendAlpha(&picture, background_color);
|
WebPBlendAlpha(&picture, background_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keep_alpha == 2) {
|
|
||||||
WebPCleanupTransparentArea(&picture);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
const double read_time = StopwatchReadAndReset(&stop_watch);
|
const double read_time = StopwatchReadAndReset(&stop_watch);
|
||||||
fprintf(stderr, "Time to read input: %.3fs\n", read_time);
|
fprintf(stderr, "Time to read input: %.3fs\n", read_time);
|
||||||
@@ -981,7 +974,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
// Open the output
|
// Open the output
|
||||||
if (out_file != NULL) {
|
if (out_file != NULL) {
|
||||||
const int use_stdout = !strcmp(out_file, "-");
|
const int use_stdout = !strcmp(out_file, "-");
|
||||||
out = use_stdout ? stdout : fopen(out_file, "wb");
|
out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
|
||||||
if (out == NULL) {
|
if (out == NULL) {
|
||||||
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
||||||
goto Error;
|
goto Error;
|
||||||
@@ -1009,7 +1002,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
picture.user_data = (void*)in_file;
|
picture.user_data = (void*)in_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress
|
// Crop & resize.
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
StopwatchReset(&stop_watch);
|
StopwatchReset(&stop_watch);
|
||||||
}
|
}
|
||||||
@@ -1026,12 +1019,22 @@ int main(int argc, const char *argv[]) {
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) {
|
||||||
|
const double preproc_time = StopwatchReadAndReset(&stop_watch);
|
||||||
|
fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time);
|
||||||
|
}
|
||||||
|
|
||||||
if (picture.extra_info_type > 0) {
|
if (picture.extra_info_type > 0) {
|
||||||
AllocExtraInfo(&picture);
|
AllocExtraInfo(&picture);
|
||||||
}
|
}
|
||||||
if (print_distortion >= 0) { // Save original picture for later comparison
|
if (print_distortion >= 0) { // Save original picture for later comparison
|
||||||
WebPPictureCopy(&picture, &original_picture);
|
WebPPictureCopy(&picture, &original_picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compress.
|
||||||
|
if (verbose) {
|
||||||
|
StopwatchReset(&stop_watch);
|
||||||
|
}
|
||||||
if (!WebPEncode(&config, &picture)) {
|
if (!WebPEncode(&config, &picture)) {
|
||||||
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
||||||
fprintf(stderr, "Error code: %d (%s)\n",
|
fprintf(stderr, "Error code: %d (%s)\n",
|
||||||
@@ -1095,25 +1098,14 @@ int main(int argc, const char *argv[]) {
|
|||||||
if (print_distortion >= 0) { // print distortion
|
if (print_distortion >= 0) { // print distortion
|
||||||
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
|
static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
|
||||||
float values[5];
|
float values[5];
|
||||||
// Comparison is performed in YUVA colorspace.
|
|
||||||
if (original_picture.use_argb &&
|
|
||||||
!WebPPictureARGBToYUVA(&original_picture, WEBP_YUV420A)) {
|
|
||||||
fprintf(stderr, "Error while converting original picture to YUVA.\n");
|
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
if (picture.use_argb &&
|
|
||||||
!WebPPictureARGBToYUVA(&picture, WEBP_YUV420A)) {
|
|
||||||
fprintf(stderr, "Error while converting compressed picture to YUVA.\n");
|
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
if (!WebPPictureDistortion(&picture, &original_picture,
|
if (!WebPPictureDistortion(&picture, &original_picture,
|
||||||
print_distortion, values)) {
|
print_distortion, values)) {
|
||||||
fprintf(stderr, "Error while computing the distortion.\n");
|
fprintf(stderr, "Error while computing the distortion.\n");
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
if (!short_output) {
|
if (!short_output) {
|
||||||
fprintf(stderr, "%s: Y:%.2f U:%.2f V:%.2f A:%.2f Total:%.2f\n",
|
fprintf(stderr, "%s: ", distortion_names[print_distortion]);
|
||||||
distortion_names[print_distortion],
|
fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n",
|
||||||
values[0], values[1], values[2], values[3], values[4]);
|
values[0], values[1], values[2], values[3], values[4]);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
|
fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
|
||||||
@@ -1126,11 +1118,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
return_value = 0;
|
return_value = 0;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
WebPMemoryWriterClear(&memory_writer);
|
WebPMemoryWriterClear(&memory_writer);
|
||||||
#else
|
|
||||||
free(memory_writer.mem);
|
|
||||||
#endif
|
|
||||||
free(picture.extra_info);
|
free(picture.extra_info);
|
||||||
MetadataFree(&metadata);
|
MetadataFree(&metadata);
|
||||||
WebPPictureFree(&picture);
|
WebPPictureFree(&picture);
|
||||||
|
|||||||
677
examples/dwebp.c
677
examples/dwebp.c
@@ -20,34 +20,13 @@
|
|||||||
#include "webp/config.h"
|
#include "webp/config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WEBP_HAVE_PNG
|
#include "../examples/example_util.h"
|
||||||
#include <png.h>
|
#include "../imageio/image_enc.h"
|
||||||
#endif
|
#include "../imageio/webpdec.h"
|
||||||
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
|
||||||
#endif
|
|
||||||
#define CINTERFACE
|
|
||||||
#define COBJMACROS
|
|
||||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
|
||||||
// code with COBJMACROS.
|
|
||||||
#include <ole2.h> // CreateStreamOnHGlobal()
|
|
||||||
#include <shlwapi.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <wincodec.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <fcntl.h> // for _O_BINARY
|
|
||||||
#include <io.h> // for _setmode()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "webp/decode.h"
|
|
||||||
#include "./example_util.h"
|
|
||||||
#include "./stopwatch.h"
|
#include "./stopwatch.h"
|
||||||
|
|
||||||
static int verbose = 0;
|
static int verbose = 0;
|
||||||
|
static int quiet = 0;
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -60,471 +39,26 @@ extern void* VP8GetCPUInfo; // opaque forward declaration.
|
|||||||
#endif
|
#endif
|
||||||
#endif // WEBP_DLL
|
#endif // WEBP_DLL
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Output types
|
|
||||||
typedef enum {
|
|
||||||
PNG = 0,
|
|
||||||
PAM,
|
|
||||||
PPM,
|
|
||||||
PGM,
|
|
||||||
BMP,
|
|
||||||
TIFF,
|
|
||||||
YUV,
|
|
||||||
ALPHA_PLANE_ONLY // this is for experimenting only
|
|
||||||
} OutputFileFormat;
|
|
||||||
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
|
||||||
|
|
||||||
#define IFS(fn) \
|
|
||||||
do { \
|
|
||||||
if (SUCCEEDED(hr)) { \
|
|
||||||
hr = (fn); \
|
|
||||||
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define MAKE_REFGUID(x) (x)
|
|
||||||
#else
|
|
||||||
#define MAKE_REFGUID(x) &(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static HRESULT CreateOutputStream(const char* out_file_name,
|
|
||||||
int write_to_mem, IStream** stream) {
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
if (write_to_mem) {
|
|
||||||
// Output to a memory buffer. This is freed when 'stream' is released.
|
|
||||||
IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
|
|
||||||
} else {
|
|
||||||
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
|
|
||||||
}
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
fprintf(stderr, "Error opening output file %s (%08lx)\n",
|
|
||||||
out_file_name, hr);
|
|
||||||
}
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
|
||||||
REFGUID container_guid,
|
|
||||||
uint8_t* rgb, int stride,
|
|
||||||
uint32_t width, uint32_t height, int has_alpha) {
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
IWICImagingFactory* factory = NULL;
|
|
||||||
IWICBitmapFrameEncode* frame = NULL;
|
|
||||||
IWICBitmapEncoder* encoder = NULL;
|
|
||||||
IStream* stream = NULL;
|
|
||||||
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
|
||||||
: GUID_WICPixelFormat24bppBGR;
|
|
||||||
|
|
||||||
IFS(CoInitialize(NULL));
|
|
||||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
|
||||||
CLSCTX_INPROC_SERVER,
|
|
||||||
MAKE_REFGUID(IID_IWICImagingFactory),
|
|
||||||
(LPVOID*)&factory));
|
|
||||||
if (hr == REGDB_E_CLASSNOTREG) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Couldn't access Windows Imaging Component (are you running "
|
|
||||||
"Windows XP SP3 or newer?). PNG support not available. "
|
|
||||||
"Use -ppm or -pgm for available PPM and PGM formats.\n");
|
|
||||||
}
|
|
||||||
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
|
|
||||||
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
|
|
||||||
&encoder));
|
|
||||||
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
|
|
||||||
WICBitmapEncoderNoCache));
|
|
||||||
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
|
|
||||||
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
|
|
||||||
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
|
|
||||||
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
|
|
||||||
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
|
|
||||||
height * stride, rgb));
|
|
||||||
IFS(IWICBitmapFrameEncode_Commit(frame));
|
|
||||||
IFS(IWICBitmapEncoder_Commit(encoder));
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && use_stdout) {
|
|
||||||
HGLOBAL image;
|
|
||||||
IFS(GetHGlobalFromStream(stream, &image));
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
DWORD mode;
|
|
||||||
const BOOL update_mode = GetConsoleMode(std_output, &mode);
|
|
||||||
const void* const image_mem = GlobalLock(image);
|
|
||||||
DWORD bytes_written = 0;
|
|
||||||
|
|
||||||
// Clear output processing if necessary, then output the image.
|
|
||||||
if (update_mode) SetConsoleMode(std_output, 0);
|
|
||||||
if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
|
|
||||||
&bytes_written, NULL) ||
|
|
||||||
bytes_written != GlobalSize(image)) {
|
|
||||||
hr = E_FAIL;
|
|
||||||
}
|
|
||||||
if (update_mode) SetConsoleMode(std_output, mode);
|
|
||||||
GlobalUnlock(image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame != NULL) IUnknown_Release(frame);
|
|
||||||
if (encoder != NULL) IUnknown_Release(encoder);
|
|
||||||
if (factory != NULL) IUnknown_Release(factory);
|
|
||||||
if (stream != NULL) IUnknown_Release(stream);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int WritePNG(const char* out_file_name, int use_stdout,
|
|
||||||
const WebPDecBuffer* const buffer) {
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
uint8_t* const rgb = buffer->u.RGBA.rgba;
|
|
||||||
const int stride = buffer->u.RGBA.stride;
|
|
||||||
const int has_alpha = (buffer->colorspace == MODE_BGRA);
|
|
||||||
|
|
||||||
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
|
|
||||||
MAKE_REFGUID(GUID_ContainerFormatPng),
|
|
||||||
rgb, stride, width, height, has_alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
|
||||||
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) {
|
|
||||||
(void)dummy; // remove variable-unused warning
|
|
||||||
longjmp(png_jmpbuf(png), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
uint8_t* const rgb = buffer->u.RGBA.rgba;
|
|
||||||
const int stride = buffer->u.RGBA.stride;
|
|
||||||
const int has_alpha = (buffer->colorspace == MODE_RGBA);
|
|
||||||
png_structp png;
|
|
||||||
png_infop info;
|
|
||||||
png_uint_32 y;
|
|
||||||
|
|
||||||
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
||||||
NULL, PNGErrorFunction, NULL);
|
|
||||||
if (png == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
info = png_create_info_struct(png);
|
|
||||||
if (info == NULL) {
|
|
||||||
png_destroy_write_struct(&png, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (setjmp(png_jmpbuf(png))) {
|
|
||||||
png_destroy_write_struct(&png, &info);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
png_init_io(png, out_file);
|
|
||||||
png_set_IHDR(png, info, width, height, 8,
|
|
||||||
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
|
||||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
||||||
PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
png_write_info(png, info);
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
png_bytep row = rgb + y * stride;
|
|
||||||
png_write_rows(png, &row, 1);
|
|
||||||
}
|
|
||||||
png_write_end(png, info);
|
|
||||||
png_destroy_write_struct(&png, &info);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
|
||||||
static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
|
||||||
(void)out_file;
|
|
||||||
(void)buffer;
|
|
||||||
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
|
||||||
"development package before building.\n");
|
|
||||||
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
const uint8_t* const rgb = buffer->u.RGBA.rgba;
|
|
||||||
const int stride = buffer->u.RGBA.stride;
|
|
||||||
const size_t bytes_per_px = alpha ? 4 : 3;
|
|
||||||
uint32_t y;
|
|
||||||
|
|
||||||
if (alpha) {
|
|
||||||
fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
|
|
||||||
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
|
||||||
} else {
|
|
||||||
fprintf(fout, "P6\n%d %d\n255\n", width, height);
|
|
||||||
}
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PutLE16(uint8_t* const dst, uint32_t value) {
|
|
||||||
dst[0] = (value >> 0) & 0xff;
|
|
||||||
dst[1] = (value >> 8) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PutLE32(uint8_t* const dst, uint32_t value) {
|
|
||||||
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
|
||||||
PutLE16(dst + 2, (value >> 16) & 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BMP_HEADER_SIZE 54
|
|
||||||
static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
||||||
const int has_alpha = (buffer->colorspace != MODE_BGR);
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
const uint8_t* const rgba = buffer->u.RGBA.rgba;
|
|
||||||
const int stride = buffer->u.RGBA.stride;
|
|
||||||
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
|
|
||||||
uint32_t y;
|
|
||||||
const uint32_t line_size = bytes_per_px * width;
|
|
||||||
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
|
|
||||||
const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
|
|
||||||
uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };
|
|
||||||
|
|
||||||
// bitmap file header
|
|
||||||
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
|
||||||
PutLE32(bmp_header + 2, total_size); // size including header
|
|
||||||
PutLE32(bmp_header + 6, 0); // reserved
|
|
||||||
PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
|
|
||||||
// bitmap info header
|
|
||||||
PutLE32(bmp_header + 14, 40); // DIB header size
|
|
||||||
PutLE32(bmp_header + 18, width); // dimensions
|
|
||||||
PutLE32(bmp_header + 22, -(int)height); // vertical flip!
|
|
||||||
PutLE16(bmp_header + 26, 1); // number of planes
|
|
||||||
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
|
||||||
PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
|
|
||||||
PutLE32(bmp_header + 34, 0); // image size (dummy)
|
|
||||||
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
|
||||||
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
|
||||||
PutLE32(bmp_header + 46, 0); // number of palette colors
|
|
||||||
PutLE32(bmp_header + 50, 0); // important color count
|
|
||||||
|
|
||||||
// TODO(skal): color profile
|
|
||||||
|
|
||||||
// write header
|
|
||||||
if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write pixel array
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// write padding zeroes
|
|
||||||
if (bmp_stride != line_size) {
|
|
||||||
const uint8_t zeroes[3] = { 0 };
|
|
||||||
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#undef BMP_HEADER_SIZE
|
|
||||||
|
|
||||||
#define NUM_IFD_ENTRIES 15
|
|
||||||
#define EXTRA_DATA_SIZE 16
|
|
||||||
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
|
|
||||||
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
|
|
||||||
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
|
|
||||||
|
|
||||||
static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
||||||
const int has_alpha = (buffer->colorspace != MODE_RGB);
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
const uint8_t* const rgba = buffer->u.RGBA.rgba;
|
|
||||||
const int stride = buffer->u.RGBA.stride;
|
|
||||||
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
|
||||||
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
|
||||||
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
|
||||||
: NUM_IFD_ENTRIES - 1;
|
|
||||||
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
|
||||||
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
|
||||||
8, 0, 0, 0, // offset to the unique IFD that follows
|
|
||||||
// IFD (offset = 8). Entries must be written in increasing tag order.
|
|
||||||
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
|
||||||
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
|
||||||
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
|
||||||
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
|
||||||
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
|
||||||
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
|
||||||
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
|
||||||
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
|
||||||
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
|
||||||
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
|
||||||
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
|
||||||
bytes_per_px, 0, 0, 0,
|
|
||||||
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
|
||||||
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
|
||||||
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
|
||||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
|
||||||
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
|
||||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
|
||||||
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
|
||||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
|
||||||
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
|
|
||||||
0, 0, 0, 0, // 190: IFD terminator
|
|
||||||
// EXTRA_DATA_OFFSET:
|
|
||||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
|
||||||
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
|
||||||
};
|
|
||||||
uint32_t y;
|
|
||||||
|
|
||||||
// Fill placeholders in IFD:
|
|
||||||
PutLE32(tiff_header + 10 + 8, width);
|
|
||||||
PutLE32(tiff_header + 22 + 8, height);
|
|
||||||
PutLE32(tiff_header + 106 + 8, height);
|
|
||||||
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
|
|
||||||
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
|
|
||||||
|
|
||||||
// write header
|
|
||||||
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// write pixel values
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef TIFF_HEADER_SIZE
|
|
||||||
#undef EXTRA_DATA_OFFSET
|
|
||||||
#undef EXTRA_DATA_SIZE
|
|
||||||
#undef NUM_IFD_ENTRIES
|
|
||||||
|
|
||||||
static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
||||||
const uint32_t width = buffer->width;
|
|
||||||
const uint32_t height = buffer->height;
|
|
||||||
const uint8_t* const a = buffer->u.YUVA.a;
|
|
||||||
const int a_stride = buffer->u.YUVA.a_stride;
|
|
||||||
uint32_t y;
|
|
||||||
assert(a != NULL);
|
|
||||||
fprintf(fout, "P5\n%d %d\n255\n", width, height);
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
if (fwrite(a + y * a_stride, width, 1, fout) != 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// format=PGM: save a grayscale PGM file using the IMC4 layout
|
|
||||||
// (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for
|
|
||||||
// viewing the samples, esp. for odd dimensions.
|
|
||||||
// format=YUV: just save the Y/U/V/A planes sequentially without header.
|
|
||||||
static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer,
|
|
||||||
OutputFileFormat format) {
|
|
||||||
const int width = buffer->width;
|
|
||||||
const int height = buffer->height;
|
|
||||||
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
|
||||||
int ok = 1;
|
|
||||||
int y;
|
|
||||||
const int pad = (format == YUV) ? 0 : 1;
|
|
||||||
const int uv_width = (width + 1) / 2;
|
|
||||||
const int uv_height = (height + 1) / 2;
|
|
||||||
const int out_stride = (width + pad) & ~pad;
|
|
||||||
const int a_height = yuv->a ? height : 0;
|
|
||||||
if (format == PGM) {
|
|
||||||
fprintf(fout, "P5\n%d %d\n255\n",
|
|
||||||
out_stride, height + uv_height + a_height);
|
|
||||||
}
|
|
||||||
for (y = 0; ok && y < height; ++y) {
|
|
||||||
ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1);
|
|
||||||
if (format == PGM) {
|
|
||||||
if (width & 1) fputc(0, fout); // padding byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (format == PGM) { // IMC4 layout
|
|
||||||
for (y = 0; ok && y < uv_height; ++y) {
|
|
||||||
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
|
|
||||||
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (y = 0; ok && y < uv_height; ++y) {
|
|
||||||
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
|
|
||||||
}
|
|
||||||
for (y = 0; ok && y < uv_height; ++y) {
|
|
||||||
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (y = 0; ok && y < a_height; ++y) {
|
|
||||||
ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1);
|
|
||||||
if (format == PGM) {
|
|
||||||
if (width & 1) fputc(0, fout); // padding byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SaveOutput(const WebPDecBuffer* const buffer,
|
static int SaveOutput(const WebPDecBuffer* const buffer,
|
||||||
OutputFileFormat format, const char* const out_file) {
|
WebPOutputFileFormat format, const char* const out_file) {
|
||||||
FILE* fout = NULL;
|
const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-");
|
||||||
int needs_open_file = 1;
|
|
||||||
const int use_stdout = !strcmp(out_file, "-");
|
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
Stopwatch stop_watch;
|
Stopwatch stop_watch;
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
StopwatchReset(&stop_watch);
|
StopwatchReset(&stop_watch);
|
||||||
}
|
}
|
||||||
|
ok = WebPSaveImage(buffer, format, out_file);
|
||||||
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
|
||||||
needs_open_file = (format != PNG);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
if (use_stdout && _setmode(_fileno(stdout), _O_BINARY) == -1) {
|
|
||||||
fprintf(stderr, "Failed to reopen stdout in O_BINARY mode.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (needs_open_file) {
|
|
||||||
fout = use_stdout ? stdout : fopen(out_file, "wb");
|
|
||||||
if (fout == NULL) {
|
|
||||||
fprintf(stderr, "Error opening output file %s\n", out_file);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format == PNG) {
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
|
||||||
ok &= WritePNG(out_file, use_stdout, buffer);
|
|
||||||
#else
|
|
||||||
ok &= WritePNG(fout, buffer);
|
|
||||||
#endif
|
|
||||||
} else if (format == PAM) {
|
|
||||||
ok &= WritePPM(fout, buffer, 1);
|
|
||||||
} else if (format == PPM) {
|
|
||||||
ok &= WritePPM(fout, buffer, 0);
|
|
||||||
} else if (format == BMP) {
|
|
||||||
ok &= WriteBMP(fout, buffer);
|
|
||||||
} else if (format == TIFF) {
|
|
||||||
ok &= WriteTIFF(fout, buffer);
|
|
||||||
} else if (format == PGM || format == YUV) {
|
|
||||||
ok &= WritePGMOrYUV(fout, buffer, format);
|
|
||||||
} else if (format == ALPHA_PLANE_ONLY) {
|
|
||||||
ok &= WriteAlphaPlane(fout, buffer);
|
|
||||||
}
|
|
||||||
if (fout != NULL && fout != stdout) {
|
|
||||||
fclose(fout);
|
|
||||||
}
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
if (!quiet) {
|
||||||
if (use_stdout) {
|
if (use_stdout) {
|
||||||
fprintf(stderr, "Saved to stdout\n");
|
fprintf(stderr, "Saved to stdout\n");
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Saved file %s\n", out_file);
|
fprintf(stderr, "Saved file %s\n", out_file);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
const double write_time = StopwatchReadAndReset(&stop_watch);
|
const double write_time = StopwatchReadAndReset(&stop_watch);
|
||||||
fprintf(stderr, "Time to write output: %.3fs\n", write_time);
|
fprintf(stderr, "Time to write output: %.3fs\n", write_time);
|
||||||
@@ -552,24 +86,21 @@ static void Help(void) {
|
|||||||
" -yuv ......... save the raw YUV samples in flat layout\n"
|
" -yuv ......... save the raw YUV samples in flat layout\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Other options are:\n"
|
" Other options are:\n"
|
||||||
" -version .... print version number and exit\n"
|
" -version ..... print version number and exit\n"
|
||||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||||
" -nofilter .... disable in-loop filtering\n"
|
" -nofilter .... disable in-loop filtering\n"
|
||||||
" -nodither .... disable dithering\n"
|
" -nodither .... disable dithering\n"
|
||||||
" -dither <d> .. dithering strength (in 0..100)\n"
|
" -dither <d> .. dithering strength (in 0..100)\n"
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
" -alpha_dither use alpha-plane dithering if needed\n"
|
" -alpha_dither use alpha-plane dithering if needed\n"
|
||||||
#endif
|
|
||||||
" -mt .......... use multi-threading\n"
|
" -mt .......... use multi-threading\n"
|
||||||
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
" -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
|
||||||
" -scale <w> <h> .......... scale the output (*after* any cropping)\n"
|
" -resize <w> <h> ......... scale the output (*after* any cropping)\n"
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
" -flip ........ flip the output vertically\n"
|
" -flip ........ flip the output vertically\n"
|
||||||
#endif
|
|
||||||
" -alpha ....... only save the alpha plane\n"
|
" -alpha ....... only save the alpha plane\n"
|
||||||
" -incremental . use incremental decoding (useful for tests)\n"
|
" -incremental . use incremental decoding (useful for tests)\n"
|
||||||
" -h ....... this help message\n"
|
" -h ........... this help message\n"
|
||||||
" -v ....... verbose (e.g. print encoding/decoding times)\n"
|
" -v ........... verbose (e.g. print encoding/decoding times)\n"
|
||||||
|
" -quiet ....... quiet mode, don't print anything\n"
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
" -noasm ....... disable all assembly optimizations\n"
|
" -noasm ....... disable all assembly optimizations\n"
|
||||||
#endif
|
#endif
|
||||||
@@ -580,6 +111,70 @@ static const char* const kFormatType[] = {
|
|||||||
"unspecified", "lossy", "lossless"
|
"unspecified", "lossy", "lossless"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
|
||||||
|
WebPOutputFileFormat format,
|
||||||
|
int use_external_memory) {
|
||||||
|
uint8_t* external_buffer = NULL;
|
||||||
|
WebPDecBuffer* const output_buffer = &config->output;
|
||||||
|
int w = config->input.width;
|
||||||
|
int h = config->input.height;
|
||||||
|
if (config->options.use_scaling) {
|
||||||
|
w = config->options.scaled_width;
|
||||||
|
h = config->options.scaled_height;
|
||||||
|
} else if (config->options.use_cropping) {
|
||||||
|
w = config->options.crop_width;
|
||||||
|
h = config->options.crop_height;
|
||||||
|
}
|
||||||
|
if (format >= RGB && format <= rgbA_4444) {
|
||||||
|
const int bpp = (format == RGB || format == BGR) ? 3
|
||||||
|
: (format == RGBA_4444 || format == rgbA_4444 ||
|
||||||
|
format == RGB_565) ? 2
|
||||||
|
: 4;
|
||||||
|
uint32_t stride = bpp * w + 7; // <- just for exercising
|
||||||
|
external_buffer = (uint8_t*)malloc(stride * h);
|
||||||
|
if (external_buffer == NULL) return NULL;
|
||||||
|
output_buffer->u.RGBA.stride = stride;
|
||||||
|
output_buffer->u.RGBA.size = stride * h;
|
||||||
|
output_buffer->u.RGBA.rgba = external_buffer;
|
||||||
|
} else { // YUV and YUVA
|
||||||
|
const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
|
||||||
|
uint8_t* tmp;
|
||||||
|
uint32_t stride = w + 3;
|
||||||
|
uint32_t uv_stride = (w + 1) / 2 + 13;
|
||||||
|
uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
|
||||||
|
+ 2 * uv_stride * (h + 1) / 2;
|
||||||
|
assert(format >= YUV && format <= YUVA);
|
||||||
|
external_buffer = (uint8_t*)malloc(total_size);
|
||||||
|
if (external_buffer == NULL) return NULL;
|
||||||
|
tmp = external_buffer;
|
||||||
|
output_buffer->u.YUVA.y = tmp;
|
||||||
|
output_buffer->u.YUVA.y_stride = stride;
|
||||||
|
output_buffer->u.YUVA.y_size = stride * h;
|
||||||
|
tmp += output_buffer->u.YUVA.y_size;
|
||||||
|
if (has_alpha) {
|
||||||
|
output_buffer->u.YUVA.a = tmp;
|
||||||
|
output_buffer->u.YUVA.a_stride = stride;
|
||||||
|
output_buffer->u.YUVA.a_size = stride * h;
|
||||||
|
tmp += output_buffer->u.YUVA.a_size;
|
||||||
|
} else {
|
||||||
|
output_buffer->u.YUVA.a = NULL;
|
||||||
|
output_buffer->u.YUVA.a_stride = 0;
|
||||||
|
}
|
||||||
|
output_buffer->u.YUVA.u = tmp;
|
||||||
|
output_buffer->u.YUVA.u_stride = uv_stride;
|
||||||
|
output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2;
|
||||||
|
tmp += output_buffer->u.YUVA.u_size;
|
||||||
|
|
||||||
|
output_buffer->u.YUVA.v = tmp;
|
||||||
|
output_buffer->u.YUVA.v_stride = uv_stride;
|
||||||
|
output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2;
|
||||||
|
tmp += output_buffer->u.YUVA.v_size;
|
||||||
|
assert(tmp <= external_buffer + total_size);
|
||||||
|
}
|
||||||
|
output_buffer->is_external_memory = use_external_memory;
|
||||||
|
return external_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
const char *in_file = NULL;
|
const char *in_file = NULL;
|
||||||
@@ -588,7 +183,11 @@ int main(int argc, const char *argv[]) {
|
|||||||
WebPDecoderConfig config;
|
WebPDecoderConfig config;
|
||||||
WebPDecBuffer* const output_buffer = &config.output;
|
WebPDecBuffer* const output_buffer = &config.output;
|
||||||
WebPBitstreamFeatures* const bitstream = &config.input;
|
WebPBitstreamFeatures* const bitstream = &config.input;
|
||||||
OutputFileFormat format = PNG;
|
WebPOutputFileFormat format = PNG;
|
||||||
|
uint8_t* external_buffer = NULL;
|
||||||
|
int use_external_memory = 0;
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
|
||||||
int incremental = 0;
|
int incremental = 0;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
@@ -598,6 +197,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (c = 1; c < argc; ++c) {
|
for (c = 1; c < argc; ++c) {
|
||||||
|
int parse_error = 0;
|
||||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||||
Help();
|
Help();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -617,6 +217,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
format = BMP;
|
format = BMP;
|
||||||
} else if (!strcmp(argv[c], "-tiff")) {
|
} else if (!strcmp(argv[c], "-tiff")) {
|
||||||
format = TIFF;
|
format = TIFF;
|
||||||
|
} else if (!strcmp(argv[c], "-quiet")) {
|
||||||
|
quiet = 1;
|
||||||
} else if (!strcmp(argv[c], "-version")) {
|
} else if (!strcmp(argv[c], "-version")) {
|
||||||
const int version = WebPGetDecoderVersion();
|
const int version = WebPGetDecoderVersion();
|
||||||
printf("%d.%d.%d\n",
|
printf("%d.%d.%d\n",
|
||||||
@@ -625,31 +227,54 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else if (!strcmp(argv[c], "-pgm")) {
|
} else if (!strcmp(argv[c], "-pgm")) {
|
||||||
format = PGM;
|
format = PGM;
|
||||||
} else if (!strcmp(argv[c], "-yuv")) {
|
} else if (!strcmp(argv[c], "-yuv")) {
|
||||||
format = YUV;
|
format = RAW_YUV;
|
||||||
|
} else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
|
||||||
|
const char* const fmt = argv[++c];
|
||||||
|
if (!strcmp(fmt, "RGB")) format = RGB;
|
||||||
|
else if (!strcmp(fmt, "RGBA")) format = RGBA;
|
||||||
|
else if (!strcmp(fmt, "BGR")) format = BGR;
|
||||||
|
else if (!strcmp(fmt, "BGRA")) format = BGRA;
|
||||||
|
else if (!strcmp(fmt, "ARGB")) format = ARGB;
|
||||||
|
else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
|
||||||
|
else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
|
||||||
|
else if (!strcmp(fmt, "rgbA")) format = rgbA;
|
||||||
|
else if (!strcmp(fmt, "bgrA")) format = bgrA;
|
||||||
|
else if (!strcmp(fmt, "Argb")) format = Argb;
|
||||||
|
else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
|
||||||
|
else if (!strcmp(fmt, "YUV")) format = YUV;
|
||||||
|
else if (!strcmp(fmt, "YUVA")) format = YUVA;
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) {
|
||||||
|
use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
parse_error |= (use_external_memory > 2 || use_external_memory < 0);
|
||||||
|
if (parse_error) {
|
||||||
|
fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]);
|
||||||
|
}
|
||||||
} else if (!strcmp(argv[c], "-mt")) {
|
} else if (!strcmp(argv[c], "-mt")) {
|
||||||
config.options.use_threads = 1;
|
config.options.use_threads = 1;
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
} else if (!strcmp(argv[c], "-alpha_dither")) {
|
} else if (!strcmp(argv[c], "-alpha_dither")) {
|
||||||
config.options.alpha_dithering_strength = 100;
|
config.options.alpha_dithering_strength = 100;
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[c], "-nodither")) {
|
} else if (!strcmp(argv[c], "-nodither")) {
|
||||||
config.options.dithering_strength = 0;
|
config.options.dithering_strength = 0;
|
||||||
} else if (!strcmp(argv[c], "-dither") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-dither") && c < argc - 1) {
|
||||||
config.options.dithering_strength = strtol(argv[++c], NULL, 0);
|
config.options.dithering_strength =
|
||||||
|
ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
||||||
config.options.use_cropping = 1;
|
config.options.use_cropping = 1;
|
||||||
config.options.crop_left = strtol(argv[++c], NULL, 0);
|
config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
config.options.crop_top = strtol(argv[++c], NULL, 0);
|
config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
config.options.crop_width = strtol(argv[++c], NULL, 0);
|
config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
config.options.crop_height = strtol(argv[++c], NULL, 0);
|
config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-scale") && c < argc - 2) {
|
} else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) &&
|
||||||
|
c < argc - 2) { // '-scale' is left for compatibility
|
||||||
config.options.use_scaling = 1;
|
config.options.use_scaling = 1;
|
||||||
config.options.scaled_width = strtol(argv[++c], NULL, 0);
|
config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
config.options.scaled_height = strtol(argv[++c], NULL, 0);
|
config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
} else if (!strcmp(argv[c], "-flip")) {
|
} else if (!strcmp(argv[c], "-flip")) {
|
||||||
config.options.flip = 1;
|
config.options.flip = 1;
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[c], "-v")) {
|
} else if (!strcmp(argv[c], "-v")) {
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
#ifndef WEBP_DLL
|
#ifndef WEBP_DLL
|
||||||
@@ -668,6 +293,11 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
in_file = argv[c];
|
in_file = argv[c];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_error) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_file == NULL) {
|
if (in_file == NULL) {
|
||||||
@@ -676,11 +306,12 @@ int main(int argc, const char *argv[]) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quiet) verbose = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
VP8StatusCode status = VP8_STATUS_OK;
|
VP8StatusCode status = VP8_STATUS_OK;
|
||||||
size_t data_size = 0;
|
size_t data_size = 0;
|
||||||
const uint8_t* data = NULL;
|
if (!LoadWebP(in_file, &data, &data_size, bitstream)) {
|
||||||
if (!ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,39 +337,68 @@ int main(int argc, const char *argv[]) {
|
|||||||
bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
|
bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
|
||||||
break;
|
break;
|
||||||
case PGM:
|
case PGM:
|
||||||
case YUV:
|
case RAW_YUV:
|
||||||
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
|
||||||
break;
|
break;
|
||||||
case ALPHA_PLANE_ONLY:
|
case ALPHA_PLANE_ONLY:
|
||||||
output_buffer->colorspace = MODE_YUVA;
|
output_buffer->colorspace = MODE_YUVA;
|
||||||
break;
|
break;
|
||||||
default:
|
// forced modes:
|
||||||
free((void*)data);
|
case RGB: output_buffer->colorspace = MODE_RGB; break;
|
||||||
return -1;
|
case RGBA: output_buffer->colorspace = MODE_RGBA; break;
|
||||||
|
case BGR: output_buffer->colorspace = MODE_BGR; break;
|
||||||
|
case BGRA: output_buffer->colorspace = MODE_BGRA; break;
|
||||||
|
case ARGB: output_buffer->colorspace = MODE_ARGB; break;
|
||||||
|
case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
|
||||||
|
case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
|
||||||
|
case rgbA: output_buffer->colorspace = MODE_rgbA; break;
|
||||||
|
case bgrA: output_buffer->colorspace = MODE_bgrA; break;
|
||||||
|
case Argb: output_buffer->colorspace = MODE_Argb; break;
|
||||||
|
case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
|
||||||
|
case YUV: output_buffer->colorspace = MODE_YUV; break;
|
||||||
|
case YUVA: output_buffer->colorspace = MODE_YUVA; break;
|
||||||
|
default: goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_external_memory > 0 && format >= RGB) {
|
||||||
|
external_buffer = AllocateExternalBuffer(&config, format,
|
||||||
|
use_external_memory);
|
||||||
|
if (external_buffer == NULL) goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Stopwatch stop_watch;
|
||||||
|
if (verbose) StopwatchReset(&stop_watch);
|
||||||
|
|
||||||
if (incremental) {
|
if (incremental) {
|
||||||
status = ExUtilDecodeWebPIncremental(data, data_size, verbose, &config);
|
status = DecodeWebPIncremental(data, data_size, &config);
|
||||||
} else {
|
} else {
|
||||||
status = ExUtilDecodeWebP(data, data_size, verbose, &config);
|
status = DecodeWebP(data, data_size, &config);
|
||||||
|
}
|
||||||
|
if (verbose) {
|
||||||
|
const double decode_time = StopwatchReadAndReset(&stop_watch);
|
||||||
|
fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free((void*)data);
|
|
||||||
ok = (status == VP8_STATUS_OK);
|
ok = (status == VP8_STATUS_OK);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
ExUtilPrintWebPError(in_file, status);
|
PrintWebPError(in_file, status);
|
||||||
goto Exit;
|
goto Exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_file != NULL) {
|
if (out_file != NULL) {
|
||||||
|
if (!quiet) {
|
||||||
fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. "
|
fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. "
|
||||||
"Now saving...\n",
|
"Now saving...\n",
|
||||||
in_file, output_buffer->width, output_buffer->height,
|
in_file, output_buffer->width, output_buffer->height,
|
||||||
bitstream->has_alpha ? " (with alpha)" : "",
|
bitstream->has_alpha ? " (with alpha)" : "",
|
||||||
kFormatType[bitstream->format]);
|
kFormatType[bitstream->format]);
|
||||||
|
}
|
||||||
ok = SaveOutput(output_buffer, format, out_file);
|
ok = SaveOutput(output_buffer, format, out_file);
|
||||||
} else {
|
} else {
|
||||||
|
if (!quiet) {
|
||||||
fprintf(stderr, "File %s can be decoded "
|
fprintf(stderr, "File %s can be decoded "
|
||||||
"(dimensions: %d x %d %s. Format: %s).\n",
|
"(dimensions: %d x %d %s. Format: %s).\n",
|
||||||
in_file, output_buffer->width, output_buffer->height,
|
in_file, output_buffer->width, output_buffer->height,
|
||||||
@@ -747,8 +407,11 @@ int main(int argc, const char *argv[]) {
|
|||||||
fprintf(stderr, "Nothing written; "
|
fprintf(stderr, "Nothing written; "
|
||||||
"use -o flag to save the result as e.g. PNG.\n");
|
"use -o flag to save the result as e.g. PNG.\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Exit:
|
Exit:
|
||||||
WebPFreeDecBuffer(output_buffer);
|
WebPFreeDecBuffer(output_buffer);
|
||||||
|
free((void*)external_buffer);
|
||||||
|
free((void*)data);
|
||||||
return ok ? 0 : -1;
|
return ok ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,203 +11,48 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "./example_util.h"
|
#include "./example_util.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "webp/decode.h"
|
|
||||||
#include "./stopwatch.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// File I/O
|
|
||||||
|
|
||||||
static const size_t kBlockSize = 16384; // default initial size
|
|
||||||
|
|
||||||
int ExUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
|
||||||
size_t max_size = 0;
|
|
||||||
size_t size = 0;
|
|
||||||
uint8_t* input = NULL;
|
|
||||||
|
|
||||||
if (data == NULL || data_size == NULL) return 0;
|
|
||||||
*data = NULL;
|
|
||||||
*data_size = 0;
|
|
||||||
|
|
||||||
while (!feof(stdin)) {
|
|
||||||
// We double the buffer size each time and read as much as possible.
|
|
||||||
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
|
|
||||||
void* const new_data = realloc(input, max_size + extra_size);
|
|
||||||
if (new_data == NULL) goto Error;
|
|
||||||
input = (uint8_t*)new_data;
|
|
||||||
max_size += extra_size;
|
|
||||||
size += fread(input + size, 1, extra_size, stdin);
|
|
||||||
if (size < max_size) break;
|
|
||||||
}
|
|
||||||
if (ferror(stdin)) goto Error;
|
|
||||||
*data = input;
|
|
||||||
*data_size = size;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
Error:
|
|
||||||
free(input);
|
|
||||||
fprintf(stderr, "Could not read from stdin\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ExUtilReadFile(const char* const file_name,
|
|
||||||
const uint8_t** data, size_t* data_size) {
|
|
||||||
int ok;
|
|
||||||
void* file_data;
|
|
||||||
size_t file_size;
|
|
||||||
FILE* in;
|
|
||||||
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
|
|
||||||
|
|
||||||
if (from_stdin) return ExUtilReadFromStdin(data, data_size);
|
|
||||||
|
|
||||||
if (data == NULL || data_size == NULL) return 0;
|
|
||||||
*data = NULL;
|
|
||||||
*data_size = 0;
|
|
||||||
|
|
||||||
in = fopen(file_name, "rb");
|
|
||||||
if (in == NULL) {
|
|
||||||
fprintf(stderr, "cannot open input file '%s'\n", file_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fseek(in, 0, SEEK_END);
|
|
||||||
file_size = ftell(in);
|
|
||||||
fseek(in, 0, SEEK_SET);
|
|
||||||
file_data = malloc(file_size);
|
|
||||||
if (file_data == NULL) return 0;
|
|
||||||
ok = (fread(file_data, file_size, 1, in) == 1);
|
|
||||||
fclose(in);
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
fprintf(stderr, "Could not read %d bytes of data from file %s\n",
|
|
||||||
(int)file_size, file_name);
|
|
||||||
free(file_data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*data = (uint8_t*)file_data;
|
|
||||||
*data_size = file_size;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ExUtilWriteFile(const char* const file_name,
|
|
||||||
const uint8_t* data, size_t data_size) {
|
|
||||||
int ok;
|
|
||||||
FILE* out;
|
|
||||||
const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
|
|
||||||
|
|
||||||
if (data == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
out = to_stdout ? stdout : fopen(file_name, "wb");
|
|
||||||
if (out == NULL) {
|
|
||||||
fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ok = (fwrite(data, data_size, 1, out) == 1);
|
|
||||||
if (out != stdout) fclose(out);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// WebP decoding
|
// String parsing
|
||||||
|
|
||||||
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
|
uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) {
|
||||||
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
char* end = NULL;
|
||||||
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u;
|
||||||
};
|
if (end == v && error != NULL && !*error) {
|
||||||
|
*error = 1;
|
||||||
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
fprintf(stderr, "Error! '%s' is not an integer.\n",
|
||||||
if (config->input.has_animation) {
|
(v != NULL) ? v : "(null)");
|
||||||
fprintf(stderr,
|
|
||||||
"Error! Decoding of an animated WebP file is not supported.\n"
|
|
||||||
" Use webpmux to extract the individual frames or\n"
|
|
||||||
" vwebp to view this image.\n");
|
|
||||||
}
|
}
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExUtilPrintWebPError(const char* const in_file, int status) {
|
int ExUtilGetInt(const char* const v, int base, int* const error) {
|
||||||
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
return (int)ExUtilGetUInt(v, base, error);
|
||||||
fprintf(stderr, "Status: %d", status);
|
|
||||||
if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
|
|
||||||
fprintf(stderr, "(%s)", kStatusMessages[status]);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExUtilLoadWebP(const char* const in_file,
|
int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
|
||||||
const uint8_t** data, size_t* data_size,
|
int n, error = 0;
|
||||||
WebPBitstreamFeatures* bitstream) {
|
for (n = 0; v != NULL && n < max_output; ++n) {
|
||||||
VP8StatusCode status;
|
const int value = ExUtilGetInt(v, base, &error);
|
||||||
WebPBitstreamFeatures local_features;
|
if (error) return -1;
|
||||||
if (!ExUtilReadFile(in_file, data, data_size)) return 0;
|
output[n] = value;
|
||||||
|
v = strchr(v, ',');
|
||||||
if (bitstream == NULL) {
|
if (v != NULL) ++v; // skip over the trailing ','
|
||||||
bitstream = &local_features;
|
|
||||||
}
|
}
|
||||||
|
return n;
|
||||||
status = WebPGetFeatures(*data, *data_size, bitstream);
|
|
||||||
if (status != VP8_STATUS_OK) {
|
|
||||||
free((void*)*data);
|
|
||||||
*data = NULL;
|
|
||||||
*data_size = 0;
|
|
||||||
ExUtilPrintWebPError(in_file, status);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
float ExUtilGetFloat(const char* const v, int* const error) {
|
||||||
|
char* end = NULL;
|
||||||
VP8StatusCode ExUtilDecodeWebP(const uint8_t* const data, size_t data_size,
|
const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f;
|
||||||
int verbose, WebPDecoderConfig* const config) {
|
if (end == v && error != NULL && !*error) {
|
||||||
Stopwatch stop_watch;
|
*error = 1;
|
||||||
VP8StatusCode status = VP8_STATUS_OK;
|
fprintf(stderr, "Error! '%s' is not a floating point number.\n",
|
||||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
(v != NULL) ? v : "(null)");
|
||||||
|
|
||||||
PrintAnimationWarning(config);
|
|
||||||
|
|
||||||
StopwatchReset(&stop_watch);
|
|
||||||
|
|
||||||
// Decoding call.
|
|
||||||
status = WebPDecode(data, data_size, config);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
const double decode_time = StopwatchReadAndReset(&stop_watch);
|
|
||||||
fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
|
|
||||||
}
|
}
|
||||||
return status;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
VP8StatusCode ExUtilDecodeWebPIncremental(
|
|
||||||
const uint8_t* const data, size_t data_size,
|
|
||||||
int verbose, WebPDecoderConfig* const config) {
|
|
||||||
Stopwatch stop_watch;
|
|
||||||
VP8StatusCode status = VP8_STATUS_OK;
|
|
||||||
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
|
||||||
|
|
||||||
PrintAnimationWarning(config);
|
|
||||||
|
|
||||||
StopwatchReset(&stop_watch);
|
|
||||||
|
|
||||||
// Decoding call.
|
|
||||||
{
|
|
||||||
WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
|
|
||||||
if (idec == NULL) {
|
|
||||||
fprintf(stderr, "Failed during WebPINewDecoder().\n");
|
|
||||||
return VP8_STATUS_OUT_OF_MEMORY;
|
|
||||||
} else {
|
|
||||||
status = WebPIUpdate(idec, data, data_size);
|
|
||||||
WebPIDelete(idec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
const double decode_time = StopwatchReadAndReset(&stop_watch);
|
|
||||||
fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|||||||
@@ -13,56 +13,27 @@
|
|||||||
#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||||
#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
|
||||||
|
|
||||||
#include "webp/decode.h"
|
#include "webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Allocates storage for entire file 'file_name' and returns contents and size
|
|
||||||
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
|
||||||
// be deleted using free().
|
|
||||||
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
|
||||||
// the function ExUtilReadFromStdin().
|
|
||||||
int ExUtilReadFile(const char* const file_name,
|
|
||||||
const uint8_t** data, size_t* data_size);
|
|
||||||
|
|
||||||
// Same as ExUtilReadFile(), but reads until EOF from stdin instead.
|
|
||||||
int ExUtilReadFromStdin(const uint8_t** data, size_t* data_size);
|
|
||||||
|
|
||||||
// Write a data segment into a file named 'file_name'. Returns true if ok.
|
|
||||||
// If 'file_name' is NULL or equal to "-", output is written to stdout.
|
|
||||||
int ExUtilWriteFile(const char* const file_name,
|
|
||||||
const uint8_t* data, size_t data_size);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// WebP decoding
|
// String parsing
|
||||||
|
|
||||||
// Prints an informative error message regarding decode failure of 'in_file'.
|
// Parses 'v' using strto(ul|l|d)(). If error is non-NULL, '*error' is set to
|
||||||
// 'status' is treated as a VP8StatusCode and if valid will be printed as a
|
// true on failure while on success it is left unmodified to allow chaining of
|
||||||
// text string.
|
// calls. An error is only printed on the first occurrence.
|
||||||
void ExUtilPrintWebPError(const char* const in_file, int status);
|
uint32_t ExUtilGetUInt(const char* const v, int base, int* const error);
|
||||||
|
int ExUtilGetInt(const char* const v, int base, int* const error);
|
||||||
|
float ExUtilGetFloat(const char* const v, int* const error);
|
||||||
|
|
||||||
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
|
// This variant of ExUtilGetInt() will parse multiple integers from a
|
||||||
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
|
// comma-separated list. Up to 'max_output' integers are parsed.
|
||||||
// Returns true on success.
|
// The result is placed in the output[] array, and the number of integers
|
||||||
int ExUtilLoadWebP(const char* const in_file,
|
// actually parsed is returned, or -1 if an error occurred.
|
||||||
const uint8_t** data, size_t* data_size,
|
int ExUtilGetInts(const char* v, int base, int max_output, int output[]);
|
||||||
WebPBitstreamFeatures* bitstream);
|
|
||||||
|
|
||||||
// Decodes the WebP contained in 'data'.
|
|
||||||
// 'config' is a structure previously initialized by WebPInitDecoderConfig().
|
|
||||||
// 'config->output' should have the desired colorspace selected. 'verbose' will
|
|
||||||
// cause decode timing to be reported.
|
|
||||||
// Returns the decoder status. On success 'config->output' will contain the
|
|
||||||
// decoded picture.
|
|
||||||
VP8StatusCode ExUtilDecodeWebP(const uint8_t* const data, size_t data_size,
|
|
||||||
int verbose, WebPDecoderConfig* const config);
|
|
||||||
|
|
||||||
// Same as ExUtilDecodeWebP(), but using the incremental decoder.
|
|
||||||
VP8StatusCode ExUtilDecodeWebPIncremental(
|
|
||||||
const uint8_t* const data, size_t data_size,
|
|
||||||
int verbose, WebPDecoderConfig* const config);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
@@ -25,182 +26,13 @@
|
|||||||
#include <gif_lib.h>
|
#include <gif_lib.h>
|
||||||
#include "webp/encode.h"
|
#include "webp/encode.h"
|
||||||
#include "webp/mux.h"
|
#include "webp/mux.h"
|
||||||
#include "./example_util.h"
|
#include "../examples/example_util.h"
|
||||||
#include "./gif2webp_util.h"
|
#include "../imageio/imageio_util.h"
|
||||||
|
#include "./gifdec.h"
|
||||||
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
|
||||||
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
|
||||||
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
|
||||||
# define LOCAL_GIF_PREREQ(maj, min) \
|
|
||||||
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
|
||||||
#else
|
|
||||||
# define LOCAL_GIF_VERSION 0
|
|
||||||
# define LOCAL_GIF_PREREQ(maj, min) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define GIF_TRANSPARENT_MASK 0x01
|
|
||||||
#define GIF_DISPOSE_MASK 0x07
|
|
||||||
#define GIF_DISPOSE_SHIFT 2
|
|
||||||
#define WHITE_COLOR 0xffffffff
|
|
||||||
#define MAX_CACHE_SIZE 30
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
static int transparent_index = -1; // Index of transparent color in the map.
|
static int transparent_index = GIF_INDEX_INVALID; // Opaque by default.
|
||||||
|
|
||||||
static void SanitizeKeyFrameIntervals(size_t* const kmin_ptr,
|
|
||||||
size_t* const kmax_ptr) {
|
|
||||||
size_t kmin = *kmin_ptr;
|
|
||||||
size_t kmax = *kmax_ptr;
|
|
||||||
int print_warning = 1;
|
|
||||||
|
|
||||||
if (kmin == 0) { // Disable keyframe insertion.
|
|
||||||
kmax = ~0;
|
|
||||||
kmin = kmax - 1;
|
|
||||||
print_warning = 0;
|
|
||||||
}
|
|
||||||
if (kmax == 0) {
|
|
||||||
kmax = ~0;
|
|
||||||
print_warning = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kmin >= kmax) {
|
|
||||||
kmin = kmax - 1;
|
|
||||||
if (print_warning) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"WARNING: Setting kmin = %d, so that kmin < kmax.\n", (int)kmin);
|
|
||||||
}
|
|
||||||
} else if (kmin < (kmax / 2 + 1)) {
|
|
||||||
// This ensures that cache.keyframe + kmin >= kmax is always true. So, we
|
|
||||||
// can flush all the frames in the ‘count_since_key_frame == kmax’ case.
|
|
||||||
kmin = (kmax / 2 + 1);
|
|
||||||
if (print_warning) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"WARNING: Setting kmin = %d, so that kmin >= kmax / 2 + 1.\n",
|
|
||||||
(int)kmin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Limit the max number of frames that are allocated.
|
|
||||||
if (kmax - kmin > MAX_CACHE_SIZE) {
|
|
||||||
kmin = kmax - MAX_CACHE_SIZE;
|
|
||||||
if (print_warning) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"WARNING: Setting kmin = %d, so that kmax - kmin <= 30.\n",
|
|
||||||
(int)kmin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*kmin_ptr = kmin;
|
|
||||||
*kmax_ptr = kmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Remap(const uint8_t* const src, const GifFileType* const gif,
|
|
||||||
uint32_t* dst, int len) {
|
|
||||||
int i;
|
|
||||||
const GifColorType* colors;
|
|
||||||
const ColorMapObject* const cmap =
|
|
||||||
gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
|
|
||||||
if (cmap == NULL) return;
|
|
||||||
colors = cmap->Colors;
|
|
||||||
|
|
||||||
for (i = 0; i < len; ++i) {
|
|
||||||
const GifColorType c = colors[src[i]];
|
|
||||||
dst[i] = (src[i] == transparent_index) ? WEBP_UTIL_TRANSPARENT_COLOR
|
|
||||||
: c.Blue | (c.Green << 8) | (c.Red << 16) | (0xff << 24);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the GIF image frame.
|
|
||||||
static int ReadFrame(GifFileType* const gif, WebPFrameRect* const gif_rect,
|
|
||||||
WebPPicture* const webp_frame) {
|
|
||||||
WebPPicture sub_image;
|
|
||||||
const GifImageDesc* const image_desc = &gif->Image;
|
|
||||||
uint32_t* dst = NULL;
|
|
||||||
uint8_t* tmp = NULL;
|
|
||||||
int ok = 0;
|
|
||||||
WebPFrameRect rect = {
|
|
||||||
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
|
|
||||||
};
|
|
||||||
*gif_rect = rect;
|
|
||||||
|
|
||||||
// Use a view for the sub-picture:
|
|
||||||
if (!WebPPictureView(webp_frame, rect.x_offset, rect.y_offset,
|
|
||||||
rect.width, rect.height, &sub_image)) {
|
|
||||||
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
|
|
||||||
rect.width, rect.height, rect.x_offset, rect.y_offset);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
dst = sub_image.argb;
|
|
||||||
|
|
||||||
tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
|
|
||||||
if (tmp == NULL) goto End;
|
|
||||||
|
|
||||||
if (image_desc->Interlace) { // Interlaced image.
|
|
||||||
// We need 4 passes, with the following offsets and jumps.
|
|
||||||
const int interlace_offsets[] = { 0, 4, 2, 1 };
|
|
||||||
const int interlace_jumps[] = { 8, 8, 4, 2 };
|
|
||||||
int pass;
|
|
||||||
for (pass = 0; pass < 4; ++pass) {
|
|
||||||
int y;
|
|
||||||
for (y = interlace_offsets[pass]; y < rect.height;
|
|
||||||
y += interlace_jumps[pass]) {
|
|
||||||
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
|
||||||
Remap(tmp, gif, dst + y * sub_image.argb_stride, rect.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // Non-interlaced image.
|
|
||||||
int y;
|
|
||||||
for (y = 0; y < rect.height; ++y) {
|
|
||||||
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
|
||||||
Remap(tmp, gif, dst + y * sub_image.argb_stride, rect.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok = 1;
|
|
||||||
|
|
||||||
End:
|
|
||||||
if (!ok) webp_frame->error_code = sub_image.error_code;
|
|
||||||
WebPPictureFree(&sub_image);
|
|
||||||
free(tmp);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GetBackgroundColor(const ColorMapObject* const color_map,
|
|
||||||
int bgcolor_idx, uint32_t* const bgcolor) {
|
|
||||||
if (transparent_index != -1 && bgcolor_idx == transparent_index) {
|
|
||||||
*bgcolor = WEBP_UTIL_TRANSPARENT_COLOR; // Special case.
|
|
||||||
return 1;
|
|
||||||
} else if (color_map == NULL || color_map->Colors == NULL
|
|
||||||
|| bgcolor_idx >= color_map->ColorCount) {
|
|
||||||
return 0; // Invalid color map or index.
|
|
||||||
} else {
|
|
||||||
const GifColorType color = color_map->Colors[bgcolor_idx];
|
|
||||||
*bgcolor = (0xff << 24)
|
|
||||||
| (color.Red << 16)
|
|
||||||
| (color.Green << 8)
|
|
||||||
| (color.Blue << 0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DisplayGifError(const GifFileType* const gif, int gif_error) {
|
|
||||||
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
|
|
||||||
#if LOCAL_GIF_PREREQ(4,2)
|
|
||||||
#if LOCAL_GIF_PREREQ(5,0)
|
|
||||||
// Static string actually, hence the const char* cast.
|
|
||||||
const char* error_str = (const char*)GifErrorString(
|
|
||||||
(gif == NULL) ? gif_error : gif->Error);
|
|
||||||
#else
|
|
||||||
const char* error_str = (const char*)GifErrorString();
|
|
||||||
(void)gif;
|
|
||||||
#endif
|
|
||||||
if (error_str == NULL) error_str = "Unknown error";
|
|
||||||
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
|
|
||||||
#else
|
|
||||||
(void)gif;
|
|
||||||
fprintf(stderr, "GIFLib Error %d: ", gif_error);
|
|
||||||
PrintGifError();
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
|
||||||
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
"WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
|
||||||
@@ -224,12 +56,16 @@ static void Help(void) {
|
|||||||
printf("Usage:\n");
|
printf("Usage:\n");
|
||||||
printf(" gif2webp [options] gif_file -o webp_file\n");
|
printf(" gif2webp [options] gif_file -o webp_file\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -h / -help ............ this help\n");
|
printf(" -h / -help ............. this help\n");
|
||||||
printf(" -lossy ................. encode image using lossy compression\n");
|
printf(" -lossy ................. encode image using lossy compression\n");
|
||||||
printf(" -mixed ................. for each frame in the image, pick lossy\n"
|
printf(" -mixed ................. for each frame in the image, pick lossy\n"
|
||||||
" or lossless compression heuristically\n");
|
" or lossless compression heuristically\n");
|
||||||
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
|
printf(" -q <float> ............. quality factor (0:small..100:big)\n");
|
||||||
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
|
||||||
|
printf(" -min_size .............. minimize output size (default:off)\n"
|
||||||
|
" lossless compression by default; can be\n"
|
||||||
|
" combined with -q, -m, -lossy or -mixed\n"
|
||||||
|
" options\n");
|
||||||
printf(" -kmin <int> ............ min distance between key frames\n");
|
printf(" -kmin <int> ............ min distance between key frames\n");
|
||||||
printf(" -kmax <int> ............ max distance between key frames\n");
|
printf(" -kmax <int> ............ max distance between key frames\n");
|
||||||
printf(" -f <int> ............... filter strength (0=off..100)\n");
|
printf(" -f <int> ............... filter strength (0=off..100)\n");
|
||||||
@@ -256,39 +92,47 @@ int main(int argc, const char *argv[]) {
|
|||||||
const char *in_file = NULL, *out_file = NULL;
|
const char *in_file = NULL, *out_file = NULL;
|
||||||
FILE* out = NULL;
|
FILE* out = NULL;
|
||||||
GifFileType* gif = NULL;
|
GifFileType* gif = NULL;
|
||||||
|
int frame_duration = 0;
|
||||||
|
int frame_timestamp = 0;
|
||||||
|
GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
|
||||||
|
|
||||||
|
WebPPicture frame; // Frame rectangle only (not disposed).
|
||||||
|
WebPPicture curr_canvas; // Not disposed.
|
||||||
|
WebPPicture prev_canvas; // Disposed.
|
||||||
|
|
||||||
|
WebPAnimEncoder* enc = NULL;
|
||||||
|
WebPAnimEncoderOptions enc_options;
|
||||||
WebPConfig config;
|
WebPConfig config;
|
||||||
WebPPicture frame;
|
|
||||||
WebPMuxFrameInfo info;
|
|
||||||
WebPMuxAnimParams anim = { WHITE_COLOR, 0 };
|
|
||||||
WebPFrameCache* cache = NULL;
|
|
||||||
|
|
||||||
int is_first_frame = 1; // Whether we are processing the first frame.
|
int is_first_frame = 1; // Whether we are processing the first frame.
|
||||||
int done;
|
int done;
|
||||||
int c;
|
int c;
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
WebPMux* mux = NULL;
|
WebPData webp_data;
|
||||||
WebPData webp_data = { NULL, 0 };
|
|
||||||
int keep_metadata = METADATA_XMP; // ICC not output by default.
|
int keep_metadata = METADATA_XMP; // ICC not output by default.
|
||||||
|
WebPData icc_data;
|
||||||
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
int stored_icc = 0; // Whether we have already stored an ICC profile.
|
||||||
int stored_xmp = 0;
|
WebPData xmp_data;
|
||||||
|
int stored_xmp = 0; // Whether we have already stored an XMP profile.
|
||||||
|
int loop_count = 0;
|
||||||
|
int stored_loop_count = 0; // Whether we have found an explicit loop count.
|
||||||
|
WebPMux* mux = NULL;
|
||||||
|
|
||||||
int default_kmin = 1; // Whether to use default kmin value.
|
int default_kmin = 1; // Whether to use default kmin value.
|
||||||
int default_kmax = 1;
|
int default_kmax = 1;
|
||||||
size_t kmin = 0;
|
|
||||||
size_t kmax = 0;
|
|
||||||
int allow_mixed = 0; // If true, each frame can be lossy or lossless.
|
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) ||
|
||||||
info.id = WEBP_CHUNK_ANMF;
|
!WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
|
||||||
info.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
!WebPPictureInit(&prev_canvas)) {
|
||||||
info.blend_method = WEBP_MUX_BLEND;
|
|
||||||
|
|
||||||
if (!WebPConfigInit(&config) || !WebPPictureInit(&frame)) {
|
|
||||||
fprintf(stderr, "Error! Version mismatch!\n");
|
fprintf(stderr, "Error! Version mismatch!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
config.lossless = 1; // Use lossless compression by default.
|
config.lossless = 1; // Use lossless compression by default.
|
||||||
config.image_hint = WEBP_HINT_GRAPH; // always low-color
|
|
||||||
|
WebPDataInit(&webp_data);
|
||||||
|
WebPDataInit(&icc_data);
|
||||||
|
WebPDataInit(&xmp_data);
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
Help();
|
Help();
|
||||||
@@ -296,6 +140,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (c = 1; c < argc; ++c) {
|
for (c = 1; c < argc; ++c) {
|
||||||
|
int parse_error = 0;
|
||||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||||
Help();
|
Help();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -304,20 +149,22 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else if (!strcmp(argv[c], "-lossy")) {
|
} else if (!strcmp(argv[c], "-lossy")) {
|
||||||
config.lossless = 0;
|
config.lossless = 0;
|
||||||
} else if (!strcmp(argv[c], "-mixed")) {
|
} else if (!strcmp(argv[c], "-mixed")) {
|
||||||
allow_mixed = 1;
|
enc_options.allow_mixed = 1;
|
||||||
config.lossless = 0;
|
config.lossless = 0;
|
||||||
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
||||||
config.quality = (float)strtod(argv[++c], NULL);
|
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
||||||
config.method = strtol(argv[++c], NULL, 0);
|
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-min_size")) {
|
||||||
|
enc_options.minimize_size = 1;
|
||||||
} else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
|
||||||
kmax = strtoul(argv[++c], NULL, 0);
|
enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
default_kmax = 0;
|
default_kmax = 0;
|
||||||
} else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
|
||||||
kmin = strtoul(argv[++c], NULL, 0);
|
enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
default_kmin = 0;
|
default_kmin = 0;
|
||||||
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
||||||
config.filter_strength = strtol(argv[++c], NULL, 0);
|
config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
|
||||||
static const struct {
|
static const struct {
|
||||||
const char* option;
|
const char* option;
|
||||||
@@ -369,8 +216,10 @@ int main(int argc, const char *argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
} else if (!strcmp(argv[c], "-quiet")) {
|
} else if (!strcmp(argv[c], "-quiet")) {
|
||||||
quiet = 1;
|
quiet = 1;
|
||||||
|
enc_options.verbose = 0;
|
||||||
} else if (!strcmp(argv[c], "-v")) {
|
} else if (!strcmp(argv[c], "-v")) {
|
||||||
verbose = 1;
|
verbose = 1;
|
||||||
|
enc_options.verbose = 1;
|
||||||
} else if (!strcmp(argv[c], "--")) {
|
} else if (!strcmp(argv[c], "--")) {
|
||||||
if (c < argc - 1) in_file = argv[++c];
|
if (c < argc - 1) in_file = argv[++c];
|
||||||
break;
|
break;
|
||||||
@@ -381,16 +230,20 @@ int main(int argc, const char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
in_file = argv[c];
|
in_file = argv[c];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_error) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appropriate default kmin, kmax values for lossy and lossless.
|
// Appropriate default kmin, kmax values for lossy and lossless.
|
||||||
if (default_kmin) {
|
if (default_kmin) {
|
||||||
kmin = config.lossless ? 9 : 3;
|
enc_options.kmin = config.lossless ? 9 : 3;
|
||||||
}
|
}
|
||||||
if (default_kmax) {
|
if (default_kmax) {
|
||||||
kmax = config.lossless ? 17 : 5;
|
enc_options.kmax = config.lossless ? 17 : 5;
|
||||||
}
|
}
|
||||||
SanitizeKeyFrameIntervals(&kmin, &kmax);
|
|
||||||
|
|
||||||
if (!WebPValidateConfig(&config)) {
|
if (!WebPValidateConfig(&config)) {
|
||||||
fprintf(stderr, "Error! Invalid configuration.\n");
|
fprintf(stderr, "Error! Invalid configuration.\n");
|
||||||
@@ -411,12 +264,6 @@ int main(int argc, const char *argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
if (gif == NULL) goto End;
|
if (gif == NULL) goto End;
|
||||||
|
|
||||||
mux = WebPMuxNew();
|
|
||||||
if (mux == NULL) {
|
|
||||||
fprintf(stderr, "ERROR: could not create a mux object.\n");
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over GIF images
|
// Loop over GIF images
|
||||||
done = 0;
|
done = 0;
|
||||||
do {
|
do {
|
||||||
@@ -425,17 +272,17 @@ int main(int argc, const char *argv[]) {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IMAGE_DESC_RECORD_TYPE: {
|
case IMAGE_DESC_RECORD_TYPE: {
|
||||||
WebPFrameRect gif_rect;
|
GIFFrameRect gif_rect;
|
||||||
GifImageDesc* const image_desc = &gif->Image;
|
GifImageDesc* const image_desc = &gif->Image;
|
||||||
|
|
||||||
if (!DGifGetImageDesc(gif)) goto End;
|
if (!DGifGetImageDesc(gif)) goto End;
|
||||||
|
|
||||||
// Fix some broken GIF global headers that report
|
|
||||||
// 0 x 0 screen dimension.
|
|
||||||
if (is_first_frame) {
|
if (is_first_frame) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight);
|
printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight);
|
||||||
}
|
}
|
||||||
|
// Fix some broken GIF global headers that report
|
||||||
|
// 0 x 0 screen dimension.
|
||||||
if (gif->SWidth == 0 || gif->SHeight == 0) {
|
if (gif->SWidth == 0 || gif->SHeight == 0) {
|
||||||
image_desc->Left = 0;
|
image_desc->Left = 0;
|
||||||
image_desc->Top = 0;
|
image_desc->Top = 0;
|
||||||
@@ -449,49 +296,62 @@ int main(int argc, const char *argv[]) {
|
|||||||
gif->SWidth, gif->SHeight);
|
gif->SWidth, gif->SHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if WEBP_MUX_ABI_VERSION > 0x0101
|
|
||||||
// Set definitive canvas size.
|
|
||||||
err = WebPMuxSetCanvasSize(mux, gif->SWidth, gif->SHeight);
|
|
||||||
if (err != WEBP_MUX_OK) {
|
|
||||||
fprintf(stderr, "Invalid canvas size %d x %d\n",
|
|
||||||
gif->SWidth, gif->SHeight);
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Allocate current buffer.
|
// Allocate current buffer.
|
||||||
frame.width = gif->SWidth;
|
frame.width = gif->SWidth;
|
||||||
frame.height = gif->SHeight;
|
frame.height = gif->SHeight;
|
||||||
frame.use_argb = 1;
|
frame.use_argb = 1;
|
||||||
if (!WebPPictureAlloc(&frame)) goto End;
|
if (!WebPPictureAlloc(&frame)) goto End;
|
||||||
WebPUtilClearPic(&frame, NULL);
|
GIFClearPic(&frame, NULL);
|
||||||
|
WebPPictureCopy(&frame, &curr_canvas);
|
||||||
|
WebPPictureCopy(&frame, &prev_canvas);
|
||||||
|
|
||||||
// Initialize cache.
|
// Background color.
|
||||||
cache = WebPFrameCacheNew(frame.width, frame.height,
|
GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
|
||||||
kmin, kmax, allow_mixed);
|
transparent_index,
|
||||||
if (cache == NULL) goto End;
|
&enc_options.anim_params.bgcolor);
|
||||||
|
|
||||||
|
// Initialize encoder.
|
||||||
|
enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height,
|
||||||
|
&enc_options);
|
||||||
|
if (enc == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error! Could not create encoder object. Possibly due to "
|
||||||
|
"a memory error.\n");
|
||||||
|
goto End;
|
||||||
}
|
}
|
||||||
|
is_first_frame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Some even more broken GIF can have sub-rect with zero width/height.
|
// Some even more broken GIF can have sub-rect with zero width/height.
|
||||||
if (image_desc->Width == 0 || image_desc->Height == 0) {
|
if (image_desc->Width == 0 || image_desc->Height == 0) {
|
||||||
image_desc->Width = gif->SWidth;
|
image_desc->Width = gif->SWidth;
|
||||||
image_desc->Height = gif->SHeight;
|
image_desc->Height = gif->SHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ReadFrame(gif, &gif_rect, &frame)) {
|
if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) {
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
// Blend frame rectangle with previous canvas to compose full canvas.
|
||||||
|
// Note that 'curr_canvas' is same as 'prev_canvas' at this point.
|
||||||
|
GIFBlendFrames(&frame, &gif_rect, &curr_canvas);
|
||||||
|
|
||||||
if (!WebPFrameCacheAddFrame(cache, &config, &gif_rect, &frame, &info)) {
|
if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) {
|
||||||
fprintf(stderr, "Error! Cannot encode frame as WebP\n");
|
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
|
||||||
fprintf(stderr, "Error code: %d\n", frame.error_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = WebPFrameCacheFlush(cache, verbose, mux);
|
// Update canvases.
|
||||||
if (err != WEBP_MUX_OK) {
|
GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas);
|
||||||
fprintf(stderr, "ERROR (%s): Could not add animation frame.\n",
|
GIFCopyPixels(&curr_canvas, &prev_canvas);
|
||||||
ErrorString(err));
|
|
||||||
goto End;
|
// Update timestamp (for next frame).
|
||||||
}
|
frame_timestamp += frame_duration;
|
||||||
is_first_frame = 0;
|
|
||||||
|
// In GIF, graphic control extensions are optional for a frame, so we
|
||||||
|
// may not get one before reading the next frame. To handle this case,
|
||||||
|
// we reset frame properties to reasonable defaults for the next frame.
|
||||||
|
orig_dispose = GIF_DISPOSE_NONE;
|
||||||
|
frame_duration = 0;
|
||||||
|
transparent_index = GIF_INDEX_INVALID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXTENSION_RECORD_TYPE: {
|
case EXTENSION_RECORD_TYPE: {
|
||||||
@@ -500,37 +360,16 @@ int main(int argc, const char *argv[]) {
|
|||||||
if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
|
if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
if (data == NULL) continue;
|
||||||
|
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
case COMMENT_EXT_FUNC_CODE: {
|
case COMMENT_EXT_FUNC_CODE: {
|
||||||
break; // Do nothing for now.
|
break; // Do nothing for now.
|
||||||
}
|
}
|
||||||
case GRAPHICS_EXT_FUNC_CODE: {
|
case GRAPHICS_EXT_FUNC_CODE: {
|
||||||
const int flags = data[1];
|
if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose,
|
||||||
const int dispose = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
|
&transparent_index)) {
|
||||||
const int delay = data[2] | (data[3] << 8); // In 10 ms units.
|
goto End;
|
||||||
if (data[0] != 4) goto End;
|
|
||||||
info.duration = delay * 10; // Duration is in 1 ms units for WebP.
|
|
||||||
if (dispose == 3) {
|
|
||||||
static int warning_printed = 0;
|
|
||||||
if (!warning_printed) {
|
|
||||||
fprintf(stderr, "WARNING: GIF_DISPOSE_RESTORE unsupported.\n");
|
|
||||||
warning_printed = 1;
|
|
||||||
}
|
|
||||||
// failsafe. TODO(urvang): emulate the correct behaviour by
|
|
||||||
// recoding the whole frame.
|
|
||||||
info.dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
|
||||||
} else {
|
|
||||||
info.dispose_method =
|
|
||||||
(dispose == 2) ? WEBP_MUX_DISPOSE_BACKGROUND
|
|
||||||
: WEBP_MUX_DISPOSE_NONE;
|
|
||||||
}
|
|
||||||
transparent_index = (flags & GIF_TRANSPARENT_MASK) ? data[4] : -1;
|
|
||||||
if (is_first_frame) {
|
|
||||||
if (!GetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
|
|
||||||
&anim.bgcolor)) {
|
|
||||||
fprintf(stderr, "GIF decode warning: invalid background color "
|
|
||||||
"index. Assuming white background.\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -541,14 +380,13 @@ int main(int argc, const char *argv[]) {
|
|||||||
if (data[0] != 11) break; // Chunk is too short
|
if (data[0] != 11) break; // Chunk is too short
|
||||||
if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
|
if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
|
||||||
!memcmp(data + 1, "ANIMEXTS1.0", 11)) {
|
!memcmp(data + 1, "ANIMEXTS1.0", 11)) {
|
||||||
// Recognize and parse Netscape2.0 NAB extension for loop count.
|
if (!GIFReadLoopCount(gif, &data, &loop_count)) {
|
||||||
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
|
goto End;
|
||||||
if (data == NULL) goto End; // Loop count sub-block missing.
|
|
||||||
if (data[0] < 3 || data[1] != 1) break; // wrong size/marker
|
|
||||||
anim.loop_count = data[2] | (data[3] << 8);
|
|
||||||
if (verbose) {
|
|
||||||
fprintf(stderr, "Loop count: %d\n", anim.loop_count);
|
|
||||||
}
|
}
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Loop count: %d\n", loop_count);
|
||||||
|
}
|
||||||
|
stored_loop_count = (loop_count != 0);
|
||||||
} else { // An extension containing metadata.
|
} else { // An extension containing metadata.
|
||||||
// We only store the first encountered chunk of each type, and
|
// We only store the first encountered chunk of each type, and
|
||||||
// only if requested by the user.
|
// only if requested by the user.
|
||||||
@@ -559,56 +397,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
!stored_icc &&
|
!stored_icc &&
|
||||||
!memcmp(data + 1, "ICCRGBG1012", 11);
|
!memcmp(data + 1, "ICCRGBG1012", 11);
|
||||||
if (is_xmp || is_icc) {
|
if (is_xmp || is_icc) {
|
||||||
const char* const fourccs[2] = { "XMP " , "ICCP" };
|
if (!GIFReadMetadata(gif, &data,
|
||||||
const char* const features[2] = { "XMP" , "ICC" };
|
is_xmp ? &xmp_data : &icc_data)) {
|
||||||
WebPData metadata = { NULL, 0 };
|
|
||||||
// Construct metadata from sub-blocks.
|
|
||||||
// Usual case (including ICC profile): In each sub-block, the
|
|
||||||
// first byte specifies its size in bytes (0 to 255) and the
|
|
||||||
// rest of the bytes contain the data.
|
|
||||||
// Special case for XMP data: In each sub-block, the first byte
|
|
||||||
// is also part of the XMP payload. XMP in GIF also has a 257
|
|
||||||
// byte padding data. See the XMP specification for details.
|
|
||||||
while (1) {
|
|
||||||
WebPData prev_metadata = metadata;
|
|
||||||
WebPData subblock;
|
|
||||||
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) {
|
|
||||||
WebPDataClear(&metadata);
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
if (data == NULL) break; // Finished.
|
|
||||||
subblock.size = is_xmp ? data[0] + 1 : data[0];
|
|
||||||
assert(subblock.size > 0);
|
|
||||||
subblock.bytes = is_xmp ? data : data + 1;
|
|
||||||
metadata.bytes =
|
|
||||||
(uint8_t*)realloc((void*)metadata.bytes,
|
|
||||||
prev_metadata.size + subblock.size);
|
|
||||||
if (metadata.bytes == NULL) {
|
|
||||||
WebPDataClear(&prev_metadata);
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
metadata.size += subblock.size;
|
|
||||||
memcpy((void*)(metadata.bytes + prev_metadata.size),
|
|
||||||
subblock.bytes, subblock.size);
|
|
||||||
}
|
|
||||||
if (is_xmp) {
|
|
||||||
// XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
|
|
||||||
const size_t xmp_pading_size = 257;
|
|
||||||
if (metadata.size > xmp_pading_size) {
|
|
||||||
metadata.size -= xmp_pading_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add metadata chunk.
|
|
||||||
err = WebPMuxSetChunk(mux, fourccs[is_icc], &metadata, 1);
|
|
||||||
if (verbose) {
|
|
||||||
fprintf(stderr, "%s size: %d\n",
|
|
||||||
features[is_icc], (int)metadata.size);
|
|
||||||
}
|
|
||||||
WebPDataClear(&metadata);
|
|
||||||
if (err != WEBP_MUX_OK) {
|
|
||||||
fprintf(stderr, "ERROR (%s): Could not set %s chunk.\n",
|
|
||||||
ErrorString(err), features[is_icc]);
|
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
if (is_icc) {
|
if (is_icc) {
|
||||||
@@ -642,38 +432,88 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
} while (!done);
|
} while (!done);
|
||||||
|
|
||||||
// Flush any pending frames.
|
// Last NULL frame.
|
||||||
err = WebPFrameCacheFlushAll(cache, verbose, mux);
|
if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) {
|
||||||
if (err != WEBP_MUX_OK) {
|
fprintf(stderr, "Error flushing WebP muxer.\n");
|
||||||
fprintf(stderr, "ERROR (%s): Could not add animation frame.\n",
|
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
|
||||||
ErrorString(err));
|
}
|
||||||
|
|
||||||
|
if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
|
||||||
|
fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish muxing
|
if (stored_loop_count || stored_icc || stored_xmp) {
|
||||||
err = WebPMuxSetAnimationParams(mux, &anim);
|
// Re-mux to add loop count and/or metadata as needed.
|
||||||
|
mux = WebPMuxCreate(&webp_data, 1);
|
||||||
|
if (mux == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
WebPDataClear(&webp_data);
|
||||||
|
|
||||||
|
if (stored_loop_count) { // Update loop count.
|
||||||
|
WebPMuxAnimParams new_params;
|
||||||
|
err = WebPMuxGetAnimationParams(mux, &new_params);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
fprintf(stderr, "ERROR (%s): Could not set animation parameters.\n",
|
fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n",
|
||||||
ErrorString(err));
|
ErrorString(err));
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
new_params.loop_count = loop_count;
|
||||||
|
err = WebPMuxSetAnimationParams(mux, &new_params);
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
fprintf(stderr, "ERROR (%s): Could not update loop count.\n",
|
||||||
|
ErrorString(err));
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stored_icc) { // Add ICCP chunk.
|
||||||
|
err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
|
||||||
|
}
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n",
|
||||||
|
ErrorString(err));
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stored_xmp) { // Add XMP chunk.
|
||||||
|
err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
|
||||||
|
}
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n",
|
||||||
|
ErrorString(err));
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = WebPMuxAssemble(mux, &webp_data);
|
err = WebPMuxAssemble(mux, &webp_data);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
fprintf(stderr, "ERROR (%s) assembling the WebP file.\n", ErrorString(err));
|
fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
|
||||||
|
"loop count/metadata.\n", ErrorString(err));
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (out_file != NULL) {
|
if (out_file != NULL) {
|
||||||
if (!ExUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
|
if (!ImgIoUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
|
||||||
fprintf(stderr, "Error writing output file: %s\n", out_file);
|
fprintf(stderr, "Error writing output file: %s\n", out_file);
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
fprintf(stderr, "Saved output file: %s\n", out_file);
|
fprintf(stderr, "Saved output file (%d bytes): %s\n",
|
||||||
|
(int)webp_data.size, out_file);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
fprintf(stderr, "Nothing written; use -o flag to save the result.\n");
|
fprintf(stderr, "Nothing written; use -o flag to save the result "
|
||||||
|
"(%d bytes).\n", (int)webp_data.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,14 +522,18 @@ int main(int argc, const char *argv[]) {
|
|||||||
gif_error = GIF_OK;
|
gif_error = GIF_OK;
|
||||||
|
|
||||||
End:
|
End:
|
||||||
WebPDataClear(&webp_data);
|
WebPDataClear(&icc_data);
|
||||||
|
WebPDataClear(&xmp_data);
|
||||||
WebPMuxDelete(mux);
|
WebPMuxDelete(mux);
|
||||||
|
WebPDataClear(&webp_data);
|
||||||
WebPPictureFree(&frame);
|
WebPPictureFree(&frame);
|
||||||
WebPFrameCacheDelete(cache);
|
WebPPictureFree(&curr_canvas);
|
||||||
|
WebPPictureFree(&prev_canvas);
|
||||||
|
WebPAnimEncoderDelete(enc);
|
||||||
if (out != NULL && out_file != NULL) fclose(out);
|
if (out != NULL && out_file != NULL) fclose(out);
|
||||||
|
|
||||||
if (gif_error != GIF_OK) {
|
if (gif_error != GIF_OK) {
|
||||||
DisplayGifError(gif, gif_error);
|
GIFDisplayError(gif, gif_error);
|
||||||
}
|
}
|
||||||
if (gif != NULL) {
|
if (gif != NULL) {
|
||||||
#if LOCAL_GIF_PREREQ(5,1)
|
#if LOCAL_GIF_PREREQ(5,1)
|
||||||
|
|||||||
@@ -1,708 +0,0 @@
|
|||||||
// 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.
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Helper structs and methods for gif2webp tool.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "webp/encode.h"
|
|
||||||
#include "./gif2webp_util.h"
|
|
||||||
|
|
||||||
#define DELTA_INFINITY 1ULL << 32
|
|
||||||
#define KEYFRAME_NONE -1
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Helper utilities.
|
|
||||||
|
|
||||||
static void ClearRectangle(WebPPicture* const picture,
|
|
||||||
int left, int top, int width, int height) {
|
|
||||||
int j;
|
|
||||||
for (j = top; j < top + height; ++j) {
|
|
||||||
uint32_t* const dst = picture->argb + j * picture->argb_stride;
|
|
||||||
int i;
|
|
||||||
for (i = left; i < left + width; ++i) {
|
|
||||||
dst[i] = WEBP_UTIL_TRANSPARENT_COLOR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebPUtilClearPic(WebPPicture* const picture,
|
|
||||||
const WebPFrameRect* const rect) {
|
|
||||||
if (rect != NULL) {
|
|
||||||
ClearRectangle(picture, rect->x_offset, rect->y_offset,
|
|
||||||
rect->width, rect->height);
|
|
||||||
} else {
|
|
||||||
ClearRectangle(picture, 0, 0, picture->width, picture->height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Also used in picture.c. Move to a common location?
|
|
||||||
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
|
||||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
|
||||||
uint8_t* dst, int dst_stride, int width, int height) {
|
|
||||||
while (height-- > 0) {
|
|
||||||
memcpy(dst, src, width);
|
|
||||||
src += src_stride;
|
|
||||||
dst += dst_stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
|
|
||||||
// to be already allocated.
|
|
||||||
static void CopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
|
|
||||||
assert(src->width == dst->width && src->height == dst->height);
|
|
||||||
CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
|
|
||||||
4 * dst->argb_stride, 4 * src->width, src->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
|
|
||||||
static void BlendPixels(const WebPPicture* const src,
|
|
||||||
const WebPFrameRect* const rect,
|
|
||||||
WebPPicture* const dst) {
|
|
||||||
int j;
|
|
||||||
assert(src->width == dst->width && src->height == dst->height);
|
|
||||||
for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
|
|
||||||
int i;
|
|
||||||
for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
|
|
||||||
const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
|
|
||||||
const int src_alpha = src_pixel >> 24;
|
|
||||||
if (src_alpha != 0) {
|
|
||||||
dst->argb[j * dst->argb_stride + i] = src_pixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace transparent pixels within 'dst_rect' of 'dst' by those in the 'src'.
|
|
||||||
static void ReduceTransparency(const WebPPicture* const src,
|
|
||||||
const WebPFrameRect* const rect,
|
|
||||||
WebPPicture* const dst) {
|
|
||||||
int i, j;
|
|
||||||
assert(src != NULL && dst != NULL && rect != NULL);
|
|
||||||
assert(src->width == dst->width && src->height == dst->height);
|
|
||||||
for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
|
|
||||||
for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
|
|
||||||
const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
|
|
||||||
const int src_alpha = src_pixel >> 24;
|
|
||||||
const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i];
|
|
||||||
const int dst_alpha = dst_pixel >> 24;
|
|
||||||
if (dst_alpha == 0 && src_alpha == 0xff) {
|
|
||||||
dst->argb[j * dst->argb_stride + i] = src_pixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace similar blocks of pixels by a 'see-through' transparent block
|
|
||||||
// with uniform average color.
|
|
||||||
static void FlattenSimilarBlocks(const WebPPicture* const src,
|
|
||||||
const WebPFrameRect* const rect,
|
|
||||||
WebPPicture* const dst) {
|
|
||||||
int i, j;
|
|
||||||
const int block_size = 8;
|
|
||||||
const int y_start = (rect->y_offset + block_size) & ~(block_size - 1);
|
|
||||||
const int y_end = (rect->y_offset + rect->height) & ~(block_size - 1);
|
|
||||||
const int x_start = (rect->x_offset + block_size) & ~(block_size - 1);
|
|
||||||
const int x_end = (rect->x_offset + rect->width) & ~(block_size - 1);
|
|
||||||
assert(src != NULL && dst != NULL && rect != NULL);
|
|
||||||
assert(src->width == dst->width && src->height == dst->height);
|
|
||||||
assert((block_size & (block_size - 1)) == 0); // must be a power of 2
|
|
||||||
// Iterate over each block and count similar pixels.
|
|
||||||
for (j = y_start; j < y_end; j += block_size) {
|
|
||||||
for (i = x_start; i < x_end; i += block_size) {
|
|
||||||
int cnt = 0;
|
|
||||||
int avg_r = 0, avg_g = 0, avg_b = 0;
|
|
||||||
int x, y;
|
|
||||||
const uint32_t* const psrc = src->argb + j * src->argb_stride + i;
|
|
||||||
uint32_t* const pdst = dst->argb + j * dst->argb_stride + i;
|
|
||||||
for (y = 0; y < block_size; ++y) {
|
|
||||||
for (x = 0; x < block_size; ++x) {
|
|
||||||
const uint32_t src_pixel = psrc[x + y * src->argb_stride];
|
|
||||||
const int alpha = src_pixel >> 24;
|
|
||||||
if (alpha == 0xff &&
|
|
||||||
src_pixel == pdst[x + y * dst->argb_stride]) {
|
|
||||||
++cnt;
|
|
||||||
avg_r += (src_pixel >> 16) & 0xff;
|
|
||||||
avg_g += (src_pixel >> 8) & 0xff;
|
|
||||||
avg_b += (src_pixel >> 0) & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we have a fully similar block, we replace it with an
|
|
||||||
// average transparent block. This compresses better in lossy mode.
|
|
||||||
if (cnt == block_size * block_size) {
|
|
||||||
const uint32_t color = (0x00 << 24) |
|
|
||||||
((avg_r / cnt) << 16) |
|
|
||||||
((avg_g / cnt) << 8) |
|
|
||||||
((avg_b / cnt) << 0);
|
|
||||||
for (y = 0; y < block_size; ++y) {
|
|
||||||
for (x = 0; x < block_size; ++x) {
|
|
||||||
pdst[x + y * dst->argb_stride] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Key frame related utilities.
|
|
||||||
|
|
||||||
// Returns true if 'curr' frame with frame rectangle 'curr_rect' is a key frame,
|
|
||||||
// that is, it can be decoded independently of 'prev' canvas.
|
|
||||||
static int IsKeyFrame(const WebPPicture* const curr,
|
|
||||||
const WebPFrameRect* const curr_rect,
|
|
||||||
const WebPPicture* const prev) {
|
|
||||||
int i, j;
|
|
||||||
int is_key_frame = 1;
|
|
||||||
|
|
||||||
// If previous canvas (with previous frame disposed) is all transparent,
|
|
||||||
// current frame is a key frame.
|
|
||||||
for (j = 0; j < prev->height; ++j) {
|
|
||||||
const uint32_t* const row = &prev->argb[j * prev->argb_stride];
|
|
||||||
for (i = 0; i < prev->width; ++i) {
|
|
||||||
if (row[i] & 0xff000000u) { // has alpha?
|
|
||||||
is_key_frame = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!is_key_frame) break;
|
|
||||||
}
|
|
||||||
if (is_key_frame) return 1;
|
|
||||||
|
|
||||||
// If current frame covers the whole canvas and does not contain any
|
|
||||||
// transparent pixels that depend on previous canvas, then current frame is
|
|
||||||
// a key frame.
|
|
||||||
if (curr_rect->width == curr->width && curr_rect->height == curr->height) {
|
|
||||||
assert(curr_rect->x_offset == 0 && curr_rect->y_offset == 0);
|
|
||||||
is_key_frame = 1;
|
|
||||||
for (j = 0; j < prev->height; ++j) {
|
|
||||||
for (i = 0; i < prev->width; ++i) {
|
|
||||||
const uint32_t prev_alpha =
|
|
||||||
(prev->argb[j * prev->argb_stride + i]) >> 24;
|
|
||||||
const uint32_t curr_alpha =
|
|
||||||
(curr->argb[j * curr->argb_stride + i]) >> 24;
|
|
||||||
if (curr_alpha != 0xff && prev_alpha != 0) {
|
|
||||||
is_key_frame = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!is_key_frame) break;
|
|
||||||
}
|
|
||||||
if (is_key_frame) return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given 'prev' frame and current frame rectangle 'rect', convert 'curr' frame
|
|
||||||
// to a key frame.
|
|
||||||
static void ConvertToKeyFrame(const WebPPicture* const prev,
|
|
||||||
WebPFrameRect* const rect,
|
|
||||||
WebPPicture* const curr) {
|
|
||||||
int j;
|
|
||||||
assert(curr->width == prev->width && curr->height == prev->height);
|
|
||||||
|
|
||||||
// Replace transparent pixels of current canvas with those from previous
|
|
||||||
// canvas (with previous frame disposed).
|
|
||||||
for (j = 0; j < curr->height; ++j) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < curr->width; ++i) {
|
|
||||||
uint32_t* const curr_pixel = curr->argb + j * curr->argb_stride + i;
|
|
||||||
const int curr_alpha = *curr_pixel >> 24;
|
|
||||||
if (curr_alpha == 0) {
|
|
||||||
*curr_pixel = prev->argb[j * prev->argb_stride + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frame rectangle now covers the whole canvas.
|
|
||||||
rect->x_offset = 0;
|
|
||||||
rect->y_offset = 0;
|
|
||||||
rect->width = curr->width;
|
|
||||||
rect->height = curr->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Encoded frame.
|
|
||||||
|
|
||||||
// Used to store two candidates of encoded data for an animation frame. One of
|
|
||||||
// the two will be chosen later.
|
|
||||||
typedef struct {
|
|
||||||
WebPMuxFrameInfo sub_frame; // Encoded frame rectangle.
|
|
||||||
WebPMuxFrameInfo key_frame; // Encoded frame if it was converted to keyframe.
|
|
||||||
} EncodedFrame;
|
|
||||||
|
|
||||||
// Release the data contained by 'encoded_frame'.
|
|
||||||
static void FrameRelease(EncodedFrame* const encoded_frame) {
|
|
||||||
if (encoded_frame != NULL) {
|
|
||||||
WebPDataClear(&encoded_frame->sub_frame.bitstream);
|
|
||||||
WebPDataClear(&encoded_frame->key_frame.bitstream);
|
|
||||||
memset(encoded_frame, 0, sizeof(*encoded_frame));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Frame cache.
|
|
||||||
|
|
||||||
// Used to store encoded frames that haven't been output yet.
|
|
||||||
struct WebPFrameCache {
|
|
||||||
EncodedFrame* encoded_frames; // Array of encoded frames.
|
|
||||||
size_t size; // Number of allocated data elements.
|
|
||||||
size_t start; // Start index.
|
|
||||||
size_t count; // Number of valid data elements.
|
|
||||||
int flush_count; // If >0, ‘flush_count’ frames starting from
|
|
||||||
// 'start' are ready to be added to mux.
|
|
||||||
int64_t best_delta; // min(canvas size - frame size) over the frames.
|
|
||||||
// Can be negative in certain cases due to
|
|
||||||
// transparent pixels in a frame.
|
|
||||||
int keyframe; // Index of selected keyframe relative to 'start'.
|
|
||||||
|
|
||||||
size_t kmin; // Min distance between key frames.
|
|
||||||
size_t kmax; // Max distance between key frames.
|
|
||||||
size_t count_since_key_frame; // Frames seen since the last key frame.
|
|
||||||
int allow_mixed; // If true, each frame can be lossy or lossless.
|
|
||||||
WebPPicture prev_canvas; // Previous canvas (properly disposed).
|
|
||||||
WebPPicture curr_canvas; // Current canvas (temporary buffer).
|
|
||||||
int is_first_frame; // True if no frames have been added to the cache
|
|
||||||
// since WebPFrameCacheNew().
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset the counters in the cache struct. Doesn't touch 'cache->encoded_frames'
|
|
||||||
// and 'cache->size'.
|
|
||||||
static void CacheReset(WebPFrameCache* const cache) {
|
|
||||||
cache->start = 0;
|
|
||||||
cache->count = 0;
|
|
||||||
cache->flush_count = 0;
|
|
||||||
cache->best_delta = DELTA_INFINITY;
|
|
||||||
cache->keyframe = KEYFRAME_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
|
||||||
size_t kmin, size_t kmax, int allow_mixed) {
|
|
||||||
WebPFrameCache* cache = (WebPFrameCache*)malloc(sizeof(*cache));
|
|
||||||
if (cache == NULL) return NULL;
|
|
||||||
CacheReset(cache);
|
|
||||||
// sanity init, so we can call WebPFrameCacheDelete():
|
|
||||||
cache->encoded_frames = NULL;
|
|
||||||
|
|
||||||
cache->is_first_frame = 1;
|
|
||||||
|
|
||||||
// Picture buffers.
|
|
||||||
if (!WebPPictureInit(&cache->prev_canvas) ||
|
|
||||||
!WebPPictureInit(&cache->curr_canvas)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
cache->prev_canvas.width = width;
|
|
||||||
cache->prev_canvas.height = height;
|
|
||||||
cache->prev_canvas.use_argb = 1;
|
|
||||||
if (!WebPPictureAlloc(&cache->prev_canvas) ||
|
|
||||||
!WebPPictureCopy(&cache->prev_canvas, &cache->curr_canvas)) {
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
WebPUtilClearPic(&cache->prev_canvas, NULL);
|
|
||||||
|
|
||||||
// Cache data.
|
|
||||||
cache->allow_mixed = allow_mixed;
|
|
||||||
cache->kmin = kmin;
|
|
||||||
cache->kmax = kmax;
|
|
||||||
cache->count_since_key_frame = 0;
|
|
||||||
assert(kmax > kmin);
|
|
||||||
cache->size = kmax - kmin;
|
|
||||||
cache->encoded_frames =
|
|
||||||
(EncodedFrame*)calloc(cache->size, sizeof(*cache->encoded_frames));
|
|
||||||
if (cache->encoded_frames == NULL) goto Err;
|
|
||||||
|
|
||||||
return cache; // All OK.
|
|
||||||
|
|
||||||
Err:
|
|
||||||
WebPFrameCacheDelete(cache);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebPFrameCacheDelete(WebPFrameCache* const cache) {
|
|
||||||
if (cache != NULL) {
|
|
||||||
if (cache->encoded_frames != NULL) {
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < cache->size; ++i) {
|
|
||||||
FrameRelease(&cache->encoded_frames[i]);
|
|
||||||
}
|
|
||||||
free(cache->encoded_frames);
|
|
||||||
}
|
|
||||||
WebPPictureFree(&cache->prev_canvas);
|
|
||||||
WebPPictureFree(&cache->curr_canvas);
|
|
||||||
free(cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic,
|
|
||||||
WebPMemoryWriter* const memory) {
|
|
||||||
pic->use_argb = 1;
|
|
||||||
pic->writer = WebPMemoryWrite;
|
|
||||||
pic->custom_ptr = memory;
|
|
||||||
if (!WebPEncode(config, pic)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GetEncodedData(const WebPMemoryWriter* const memory,
|
|
||||||
WebPData* const encoded_data) {
|
|
||||||
encoded_data->bytes = memory->mem;
|
|
||||||
encoded_data->size = memory->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold.
|
|
||||||
#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold.
|
|
||||||
#define MAX_COLOR_COUNT 256 // Power of 2 greater than MAX_COLORS_LOSSLESS.
|
|
||||||
#define HASH_SIZE (MAX_COLOR_COUNT * 4)
|
|
||||||
#define HASH_RIGHT_SHIFT 22 // 32 - log2(HASH_SIZE).
|
|
||||||
|
|
||||||
// TODO(urvang): Also used in enc/vp8l.c. Move to utils.
|
|
||||||
// If the number of colors in the 'pic' is at least MAX_COLOR_COUNT, return
|
|
||||||
// MAX_COLOR_COUNT. Otherwise, return the exact number of colors in the 'pic'.
|
|
||||||
static int GetColorCount(const WebPPicture* const pic) {
|
|
||||||
int x, y;
|
|
||||||
int num_colors = 0;
|
|
||||||
uint8_t in_use[HASH_SIZE] = { 0 };
|
|
||||||
uint32_t colors[HASH_SIZE];
|
|
||||||
static const uint32_t kHashMul = 0x1e35a7bd;
|
|
||||||
const uint32_t* argb = pic->argb;
|
|
||||||
const int width = pic->width;
|
|
||||||
const int height = pic->height;
|
|
||||||
uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
|
|
||||||
|
|
||||||
for (y = 0; y < height; ++y) {
|
|
||||||
for (x = 0; x < width; ++x) {
|
|
||||||
int key;
|
|
||||||
if (argb[x] == last_pix) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
last_pix = argb[x];
|
|
||||||
key = (kHashMul * last_pix) >> HASH_RIGHT_SHIFT;
|
|
||||||
while (1) {
|
|
||||||
if (!in_use[key]) {
|
|
||||||
colors[key] = last_pix;
|
|
||||||
in_use[key] = 1;
|
|
||||||
++num_colors;
|
|
||||||
if (num_colors >= MAX_COLOR_COUNT) {
|
|
||||||
return MAX_COLOR_COUNT; // Exact count not needed.
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (colors[key] == last_pix) {
|
|
||||||
break; // The color is already there.
|
|
||||||
} else {
|
|
||||||
// Some other color sits here, so do linear conflict resolution.
|
|
||||||
++key;
|
|
||||||
key &= (HASH_SIZE - 1); // Key mask.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argb += pic->argb_stride;
|
|
||||||
}
|
|
||||||
return num_colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef MAX_COLOR_COUNT
|
|
||||||
#undef HASH_SIZE
|
|
||||||
#undef HASH_RIGHT_SHIFT
|
|
||||||
|
|
||||||
static WebPEncodingError SetFrame(const WebPConfig* const config,
|
|
||||||
int allow_mixed, int is_key_frame,
|
|
||||||
const WebPPicture* const prev_canvas,
|
|
||||||
WebPPicture* const frame,
|
|
||||||
const WebPFrameRect* const rect,
|
|
||||||
const WebPMuxFrameInfo* const info,
|
|
||||||
WebPPicture* const sub_frame,
|
|
||||||
EncodedFrame* encoded_frame) {
|
|
||||||
WebPEncodingError error_code = VP8_ENC_OK;
|
|
||||||
int try_lossless;
|
|
||||||
int try_lossy;
|
|
||||||
int try_both;
|
|
||||||
WebPMemoryWriter mem1, mem2;
|
|
||||||
WebPData* encoded_data;
|
|
||||||
WebPMuxFrameInfo* const dst =
|
|
||||||
is_key_frame ? &encoded_frame->key_frame : &encoded_frame->sub_frame;
|
|
||||||
*dst = *info;
|
|
||||||
encoded_data = &dst->bitstream;
|
|
||||||
WebPMemoryWriterInit(&mem1);
|
|
||||||
WebPMemoryWriterInit(&mem2);
|
|
||||||
|
|
||||||
if (!allow_mixed) {
|
|
||||||
try_lossless = config->lossless;
|
|
||||||
try_lossy = !try_lossless;
|
|
||||||
} else { // Use a heuristic for trying lossless and/or lossy compression.
|
|
||||||
const int num_colors = GetColorCount(sub_frame);
|
|
||||||
try_lossless = (num_colors < MAX_COLORS_LOSSLESS);
|
|
||||||
try_lossy = (num_colors >= MIN_COLORS_LOSSY);
|
|
||||||
}
|
|
||||||
try_both = try_lossless && try_lossy;
|
|
||||||
|
|
||||||
if (try_lossless) {
|
|
||||||
WebPConfig config_ll = *config;
|
|
||||||
config_ll.lossless = 1;
|
|
||||||
if (!EncodeFrame(&config_ll, sub_frame, &mem1)) {
|
|
||||||
error_code = sub_frame->error_code;
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try_lossy) {
|
|
||||||
WebPConfig config_lossy = *config;
|
|
||||||
config_lossy.lossless = 0;
|
|
||||||
if (!is_key_frame) {
|
|
||||||
// For lossy compression of a frame, it's better to replace transparent
|
|
||||||
// pixels of 'curr' with actual RGB values, whenever possible.
|
|
||||||
ReduceTransparency(prev_canvas, rect, frame);
|
|
||||||
// TODO(later): Investigate if this helps lossless compression as well.
|
|
||||||
FlattenSimilarBlocks(prev_canvas, rect, frame);
|
|
||||||
}
|
|
||||||
if (!EncodeFrame(&config_lossy, sub_frame, &mem2)) {
|
|
||||||
error_code = sub_frame->error_code;
|
|
||||||
goto Err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try_both) { // Pick the encoding with smallest size.
|
|
||||||
// TODO(later): Perhaps a rough SSIM/PSNR produced by the encoder should
|
|
||||||
// also be a criteria, in addition to sizes.
|
|
||||||
if (mem1.size <= mem2.size) {
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
WebPMemoryWriterClear(&mem2);
|
|
||||||
#else
|
|
||||||
free(mem2.mem);
|
|
||||||
memset(&mem2, 0, sizeof(mem2));
|
|
||||||
#endif
|
|
||||||
GetEncodedData(&mem1, encoded_data);
|
|
||||||
} else {
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
WebPMemoryWriterClear(&mem1);
|
|
||||||
#else
|
|
||||||
free(mem1.mem);
|
|
||||||
memset(&mem1, 0, sizeof(mem1));
|
|
||||||
#endif
|
|
||||||
GetEncodedData(&mem2, encoded_data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GetEncodedData(try_lossless ? &mem1 : &mem2, encoded_data);
|
|
||||||
}
|
|
||||||
return error_code;
|
|
||||||
|
|
||||||
Err:
|
|
||||||
#if WEBP_ENCODER_ABI_VERSION > 0x0202
|
|
||||||
WebPMemoryWriterClear(&mem1);
|
|
||||||
WebPMemoryWriterClear(&mem2);
|
|
||||||
#else
|
|
||||||
free(mem1.mem);
|
|
||||||
free(mem2.mem);
|
|
||||||
#endif
|
|
||||||
return error_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef MIN_COLORS_LOSSY
|
|
||||||
#undef MAX_COLORS_LOSSLESS
|
|
||||||
|
|
||||||
// Returns cached frame at given 'position' index.
|
|
||||||
static EncodedFrame* CacheGetFrame(const WebPFrameCache* const cache,
|
|
||||||
size_t position) {
|
|
||||||
assert(cache->start + position < cache->size);
|
|
||||||
return &cache->encoded_frames[cache->start + position];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the penalty incurred if we encode given frame as a key frame
|
|
||||||
// instead of a sub-frame.
|
|
||||||
static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
|
|
||||||
return ((int64_t)encoded_frame->key_frame.bitstream.size -
|
|
||||||
encoded_frame->sub_frame.bitstream.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DisposeFrame(WebPMuxAnimDispose dispose_method,
|
|
||||||
const WebPFrameRect* const gif_rect,
|
|
||||||
WebPPicture* const frame, WebPPicture* const canvas) {
|
|
||||||
if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
|
||||||
WebPUtilClearPic(frame, NULL);
|
|
||||||
WebPUtilClearPic(canvas, gif_rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
|
||||||
const WebPConfig* const config,
|
|
||||||
const WebPFrameRect* const orig_rect_ptr,
|
|
||||||
WebPPicture* const frame,
|
|
||||||
WebPMuxFrameInfo* const info) {
|
|
||||||
int ok = 0;
|
|
||||||
WebPEncodingError error_code = VP8_ENC_OK;
|
|
||||||
WebPFrameRect rect;
|
|
||||||
WebPPicture sub_image; // View extracted from 'frame' with rectangle 'rect'.
|
|
||||||
WebPPicture* const prev_canvas = &cache->prev_canvas;
|
|
||||||
const size_t position = cache->count;
|
|
||||||
const int allow_mixed = cache->allow_mixed;
|
|
||||||
EncodedFrame* const encoded_frame = CacheGetFrame(cache, position);
|
|
||||||
WebPFrameRect orig_rect;
|
|
||||||
assert(position < cache->size);
|
|
||||||
|
|
||||||
if (frame == NULL || info == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orig_rect_ptr == NULL) {
|
|
||||||
orig_rect.width = frame->width;
|
|
||||||
orig_rect.height = frame->height;
|
|
||||||
orig_rect.x_offset = 0;
|
|
||||||
orig_rect.y_offset = 0;
|
|
||||||
} else {
|
|
||||||
orig_rect = *orig_rect_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snap to even offsets (and adjust dimensions if needed).
|
|
||||||
rect = orig_rect;
|
|
||||||
rect.width += (rect.x_offset & 1);
|
|
||||||
rect.height += (rect.y_offset & 1);
|
|
||||||
rect.x_offset &= ~1;
|
|
||||||
rect.y_offset &= ~1;
|
|
||||||
|
|
||||||
if (!WebPPictureView(frame, rect.x_offset, rect.y_offset,
|
|
||||||
rect.width, rect.height, &sub_image)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
info->x_offset = rect.x_offset;
|
|
||||||
info->y_offset = rect.y_offset;
|
|
||||||
|
|
||||||
++cache->count;
|
|
||||||
|
|
||||||
if (cache->is_first_frame || IsKeyFrame(frame, &rect, prev_canvas)) {
|
|
||||||
// Add this as a key frame.
|
|
||||||
error_code = SetFrame(config, allow_mixed, 1, NULL, NULL, NULL,
|
|
||||||
info, &sub_image, encoded_frame);
|
|
||||||
if (error_code != VP8_ENC_OK) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
cache->keyframe = position;
|
|
||||||
cache->flush_count = cache->count;
|
|
||||||
cache->count_since_key_frame = 0;
|
|
||||||
// Update prev_canvas by simply copying from 'curr'.
|
|
||||||
CopyPixels(frame, prev_canvas);
|
|
||||||
} else {
|
|
||||||
++cache->count_since_key_frame;
|
|
||||||
if (cache->count_since_key_frame <= cache->kmin) {
|
|
||||||
// Add this as a frame rectangle.
|
|
||||||
error_code = SetFrame(config, allow_mixed, 0, prev_canvas, frame,
|
|
||||||
&rect, info, &sub_image, encoded_frame);
|
|
||||||
if (error_code != VP8_ENC_OK) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
cache->flush_count = cache->count;
|
|
||||||
// Update prev_canvas by blending 'curr' into it.
|
|
||||||
BlendPixels(frame, &orig_rect, prev_canvas);
|
|
||||||
} else {
|
|
||||||
WebPPicture full_image;
|
|
||||||
WebPMuxFrameInfo full_image_info;
|
|
||||||
int64_t curr_delta;
|
|
||||||
|
|
||||||
// Add frame rectangle to cache.
|
|
||||||
error_code = SetFrame(config, allow_mixed, 0, prev_canvas, frame, &rect,
|
|
||||||
info, &sub_image, encoded_frame);
|
|
||||||
if (error_code != VP8_ENC_OK) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to a key frame.
|
|
||||||
CopyPixels(frame, &cache->curr_canvas);
|
|
||||||
ConvertToKeyFrame(prev_canvas, &rect, &cache->curr_canvas);
|
|
||||||
if (!WebPPictureView(&cache->curr_canvas, rect.x_offset, rect.y_offset,
|
|
||||||
rect.width, rect.height, &full_image)) {
|
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
full_image_info = *info;
|
|
||||||
full_image_info.x_offset = rect.x_offset;
|
|
||||||
full_image_info.y_offset = rect.y_offset;
|
|
||||||
|
|
||||||
// Add key frame to cache, too.
|
|
||||||
error_code = SetFrame(config, allow_mixed, 1, NULL, NULL, NULL,
|
|
||||||
&full_image_info, &full_image, encoded_frame);
|
|
||||||
WebPPictureFree(&full_image);
|
|
||||||
if (error_code != VP8_ENC_OK) goto End;
|
|
||||||
|
|
||||||
// Analyze size difference of the two variants.
|
|
||||||
curr_delta = KeyFramePenalty(encoded_frame);
|
|
||||||
if (curr_delta <= cache->best_delta) { // Pick this as keyframe.
|
|
||||||
cache->keyframe = position;
|
|
||||||
cache->best_delta = curr_delta;
|
|
||||||
cache->flush_count = cache->count - 1; // We can flush previous frames.
|
|
||||||
}
|
|
||||||
if (cache->count_since_key_frame == cache->kmax) {
|
|
||||||
cache->flush_count = cache->count;
|
|
||||||
cache->count_since_key_frame = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update prev_canvas by simply copying from 'curr_canvas'.
|
|
||||||
CopyPixels(&cache->curr_canvas, prev_canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposeFrame(info->dispose_method, &orig_rect, frame, prev_canvas);
|
|
||||||
|
|
||||||
cache->is_first_frame = 0;
|
|
||||||
ok = 1;
|
|
||||||
|
|
||||||
End:
|
|
||||||
WebPPictureFree(&sub_image);
|
|
||||||
if (!ok) {
|
|
||||||
FrameRelease(encoded_frame);
|
|
||||||
--cache->count; // We reset the count, as the frame addition failed.
|
|
||||||
}
|
|
||||||
frame->error_code = error_code; // report error_code
|
|
||||||
assert(ok || error_code != VP8_ENC_OK);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPMuxError WebPFrameCacheFlush(WebPFrameCache* const cache, int verbose,
|
|
||||||
WebPMux* const mux) {
|
|
||||||
while (cache->flush_count > 0) {
|
|
||||||
WebPMuxFrameInfo* info;
|
|
||||||
WebPMuxError err;
|
|
||||||
EncodedFrame* const curr = CacheGetFrame(cache, 0);
|
|
||||||
// Pick frame or full canvas.
|
|
||||||
if (cache->keyframe == 0) {
|
|
||||||
info = &curr->key_frame;
|
|
||||||
info->blend_method = WEBP_MUX_NO_BLEND;
|
|
||||||
cache->keyframe = KEYFRAME_NONE;
|
|
||||||
cache->best_delta = DELTA_INFINITY;
|
|
||||||
} else {
|
|
||||||
info = &curr->sub_frame;
|
|
||||||
info->blend_method = WEBP_MUX_BLEND;
|
|
||||||
}
|
|
||||||
// Add to mux.
|
|
||||||
err = WebPMuxPushFrame(mux, info, 1);
|
|
||||||
if (err != WEBP_MUX_OK) return err;
|
|
||||||
if (verbose) {
|
|
||||||
printf("Added frame. offset:%d,%d duration:%d dispose:%d blend:%d\n",
|
|
||||||
info->x_offset, info->y_offset, info->duration,
|
|
||||||
info->dispose_method, info->blend_method);
|
|
||||||
}
|
|
||||||
FrameRelease(curr);
|
|
||||||
++cache->start;
|
|
||||||
--cache->flush_count;
|
|
||||||
--cache->count;
|
|
||||||
if (cache->keyframe != KEYFRAME_NONE) --cache->keyframe;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache->count == 0) CacheReset(cache);
|
|
||||||
return WEBP_MUX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPMuxError WebPFrameCacheFlushAll(WebPFrameCache* const cache, int verbose,
|
|
||||||
WebPMux* const mux) {
|
|
||||||
cache->flush_count = cache->count; // Force flushing of all frames.
|
|
||||||
return WebPFrameCacheFlush(cache, verbose, mux);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
// 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.
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Helper structs and methods for gif2webp tool.
|
|
||||||
//
|
|
||||||
// Author: Urvang (urvang@google.com)
|
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_GIF2WEBP_UTIL_H_
|
|
||||||
#define WEBP_EXAMPLES_GIF2WEBP_UTIL_H_
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "webp/mux.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Helper utilities.
|
|
||||||
|
|
||||||
#define WEBP_UTIL_TRANSPARENT_COLOR 0x00ffffff
|
|
||||||
|
|
||||||
struct WebPPicture;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int x_offset, y_offset, width, height;
|
|
||||||
} WebPFrameRect;
|
|
||||||
|
|
||||||
// Clear pixels in 'picture' within given 'rect' to transparent color.
|
|
||||||
void WebPUtilClearPic(struct WebPPicture* const picture,
|
|
||||||
const WebPFrameRect* const rect);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Frame cache.
|
|
||||||
|
|
||||||
typedef struct WebPFrameCache WebPFrameCache;
|
|
||||||
|
|
||||||
// Given the minimum distance between key frames 'kmin' and maximum distance
|
|
||||||
// between key frames 'kmax', returns an appropriately allocated cache object.
|
|
||||||
// If 'allow_mixed' is true, the subsequent calls to WebPFrameCacheAddFrame()
|
|
||||||
// will heuristically pick lossy or lossless compression for each frame.
|
|
||||||
// Use WebPFrameCacheDelete() to deallocate the 'cache'.
|
|
||||||
WebPFrameCache* WebPFrameCacheNew(int width, int height,
|
|
||||||
size_t kmin, size_t kmax, int allow_mixed);
|
|
||||||
|
|
||||||
// Release all the frame data from 'cache' and free 'cache'.
|
|
||||||
void WebPFrameCacheDelete(WebPFrameCache* const cache);
|
|
||||||
|
|
||||||
// Given an image described by 'frame', 'info' and 'orig_rect', optimize it for
|
|
||||||
// WebP, encode it and add it to 'cache'. 'orig_rect' can be NULL.
|
|
||||||
// This takes care of frame disposal too, according to 'info->dispose_method'.
|
|
||||||
// Returns false in case of error (and sets frame->error_code accordingly).
|
|
||||||
int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
|
|
||||||
const WebPConfig* const config,
|
|
||||||
const WebPFrameRect* const orig_rect,
|
|
||||||
WebPPicture* const frame,
|
|
||||||
WebPMuxFrameInfo* const info);
|
|
||||||
|
|
||||||
// Flush the *ready* frames from cache and add them to 'mux'. If 'verbose' is
|
|
||||||
// true, prints the information about these frames.
|
|
||||||
WebPMuxError WebPFrameCacheFlush(WebPFrameCache* const cache, int verbose,
|
|
||||||
WebPMux* const mux);
|
|
||||||
|
|
||||||
// Similar to 'WebPFrameCacheFlushFrames()', but flushes *all* the frames.
|
|
||||||
WebPMuxError WebPFrameCacheFlushAll(WebPFrameCache* const cache, int verbose,
|
|
||||||
WebPMux* const mux);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_GIF2WEBP_UTIL_H_
|
|
||||||
410
examples/gifdec.c
Normal file
410
examples/gifdec.c
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GIF decode.
|
||||||
|
|
||||||
|
#include "./gifdec.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef WEBP_HAVE_GIF
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "webp/encode.h"
|
||||||
|
#include "webp/mux_types.h"
|
||||||
|
|
||||||
|
#define GIF_TRANSPARENT_COLOR 0x00000000u
|
||||||
|
#define GIF_WHITE_COLOR 0xffffffffu
|
||||||
|
#define GIF_TRANSPARENT_MASK 0x01
|
||||||
|
#define GIF_DISPOSE_MASK 0x07
|
||||||
|
#define GIF_DISPOSE_SHIFT 2
|
||||||
|
|
||||||
|
// from utils/utils.h
|
||||||
|
extern void WebPCopyPlane(const uint8_t* src, int src_stride,
|
||||||
|
uint8_t* dst, int dst_stride,
|
||||||
|
int width, int height);
|
||||||
|
extern void WebPCopyPixels(const WebPPicture* const src,
|
||||||
|
WebPPicture* const dst);
|
||||||
|
|
||||||
|
void GIFGetBackgroundColor(const ColorMapObject* const color_map,
|
||||||
|
int bgcolor_index, int transparent_index,
|
||||||
|
uint32_t* const bgcolor) {
|
||||||
|
if (transparent_index != GIF_INDEX_INVALID &&
|
||||||
|
bgcolor_index == transparent_index) {
|
||||||
|
*bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
|
||||||
|
} else if (color_map == NULL || color_map->Colors == NULL
|
||||||
|
|| bgcolor_index >= color_map->ColorCount) {
|
||||||
|
*bgcolor = GIF_WHITE_COLOR;
|
||||||
|
fprintf(stderr,
|
||||||
|
"GIF decode warning: invalid background color index. Assuming "
|
||||||
|
"white background.\n");
|
||||||
|
} else {
|
||||||
|
const GifColorType color = color_map->Colors[bgcolor_index];
|
||||||
|
*bgcolor = (0xffu << 24)
|
||||||
|
| (color.Red << 16)
|
||||||
|
| (color.Green << 8)
|
||||||
|
| (color.Blue << 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
|
||||||
|
GIFDisposeMethod* const dispose,
|
||||||
|
int* const transparent_index) {
|
||||||
|
const int flags = buf[1];
|
||||||
|
const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
|
||||||
|
const int duration_raw = buf[2] | (buf[3] << 8); // In 10 ms units.
|
||||||
|
if (buf[0] != 4) return 0;
|
||||||
|
*duration = duration_raw * 10; // Duration is in 1 ms units.
|
||||||
|
switch (dispose_raw) {
|
||||||
|
case 3:
|
||||||
|
*dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*dispose = GIF_DISPOSE_BACKGROUND;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
*dispose = GIF_DISPOSE_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*transparent_index =
|
||||||
|
(flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Remap(const GifFileType* const gif, const uint8_t* const src,
|
||||||
|
int len, int transparent_index, uint32_t* dst) {
|
||||||
|
int i;
|
||||||
|
const GifColorType* colors;
|
||||||
|
const ColorMapObject* const cmap =
|
||||||
|
gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
|
||||||
|
if (cmap == NULL) return 1;
|
||||||
|
if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
|
||||||
|
colors = cmap->Colors;
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
if (src[i] == transparent_index) {
|
||||||
|
dst[i] = GIF_TRANSPARENT_COLOR;
|
||||||
|
} else if (src[i] < cmap->ColorCount) {
|
||||||
|
const GifColorType c = colors[src[i]];
|
||||||
|
dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadFrame(GifFileType* const gif, int transparent_index,
|
||||||
|
GIFFrameRect* const gif_rect, WebPPicture* const picture) {
|
||||||
|
WebPPicture sub_image;
|
||||||
|
const GifImageDesc* const image_desc = &gif->Image;
|
||||||
|
uint32_t* dst = NULL;
|
||||||
|
uint8_t* tmp = NULL;
|
||||||
|
const GIFFrameRect rect = {
|
||||||
|
image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
|
||||||
|
};
|
||||||
|
const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
|
||||||
|
int ok = 0;
|
||||||
|
*gif_rect = rect;
|
||||||
|
|
||||||
|
if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
|
||||||
|
fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a view for the sub-picture:
|
||||||
|
if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
|
||||||
|
rect.width, rect.height, &sub_image)) {
|
||||||
|
fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
|
||||||
|
rect.width, rect.height, rect.x_offset, rect.y_offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dst = sub_image.argb;
|
||||||
|
|
||||||
|
tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
|
||||||
|
if (tmp == NULL) goto End;
|
||||||
|
|
||||||
|
if (image_desc->Interlace) { // Interlaced image.
|
||||||
|
// We need 4 passes, with the following offsets and jumps.
|
||||||
|
const int interlace_offsets[] = { 0, 4, 2, 1 };
|
||||||
|
const int interlace_jumps[] = { 8, 8, 4, 2 };
|
||||||
|
int pass;
|
||||||
|
for (pass = 0; pass < 4; ++pass) {
|
||||||
|
const size_t stride = (size_t)sub_image.argb_stride;
|
||||||
|
int y = interlace_offsets[pass];
|
||||||
|
uint32_t* row = dst + y * stride;
|
||||||
|
const size_t jump = interlace_jumps[pass] * stride;
|
||||||
|
for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
|
||||||
|
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
||||||
|
if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // Non-interlaced image.
|
||||||
|
int y;
|
||||||
|
uint32_t* ptr = dst;
|
||||||
|
for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
|
||||||
|
if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
|
||||||
|
if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = 1;
|
||||||
|
|
||||||
|
End:
|
||||||
|
if (!ok) picture->error_code = sub_image.error_code;
|
||||||
|
WebPPictureFree(&sub_image);
|
||||||
|
free(tmp);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
|
||||||
|
int* const loop_count) {
|
||||||
|
assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
|
||||||
|
!memcmp(*buf + 1, "ANIMEXTS1.0", 11));
|
||||||
|
if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*buf == NULL) {
|
||||||
|
return 0; // Loop count sub-block missing.
|
||||||
|
}
|
||||||
|
if ((*buf)[0] < 3 || (*buf)[1] != 1) {
|
||||||
|
return 0; // wrong size/marker
|
||||||
|
}
|
||||||
|
*loop_count = (*buf)[2] | ((*buf)[3] << 8);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
|
||||||
|
WebPData* const metadata) {
|
||||||
|
const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
|
||||||
|
const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
|
||||||
|
assert(is_xmp || is_icc);
|
||||||
|
(void)is_icc; // silence unused warning.
|
||||||
|
// Construct metadata from sub-blocks.
|
||||||
|
// Usual case (including ICC profile): In each sub-block, the
|
||||||
|
// first byte specifies its size in bytes (0 to 255) and the
|
||||||
|
// rest of the bytes contain the data.
|
||||||
|
// Special case for XMP data: In each sub-block, the first byte
|
||||||
|
// is also part of the XMP payload. XMP in GIF also has a 257
|
||||||
|
// byte padding data. See the XMP specification for details.
|
||||||
|
while (1) {
|
||||||
|
WebPData subblock;
|
||||||
|
const uint8_t* tmp;
|
||||||
|
if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (*buf == NULL) break; // Finished.
|
||||||
|
subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
|
||||||
|
assert(subblock.size > 0);
|
||||||
|
subblock.bytes = is_xmp ? *buf : *buf + 1;
|
||||||
|
// Note: We store returned value in 'tmp' first, to avoid
|
||||||
|
// leaking old memory in metadata->bytes on error.
|
||||||
|
tmp = (uint8_t*)realloc((void*)metadata->bytes,
|
||||||
|
metadata->size + subblock.size);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy((void*)(tmp + metadata->size),
|
||||||
|
subblock.bytes, subblock.size);
|
||||||
|
metadata->bytes = tmp;
|
||||||
|
metadata->size += subblock.size;
|
||||||
|
}
|
||||||
|
if (is_xmp) {
|
||||||
|
// XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
|
||||||
|
const size_t xmp_pading_size = 257;
|
||||||
|
if (metadata->size > xmp_pading_size) {
|
||||||
|
metadata->size -= xmp_pading_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearRectangle(WebPPicture* const picture,
|
||||||
|
int left, int top, int width, int height) {
|
||||||
|
int i, j;
|
||||||
|
const size_t stride = picture->argb_stride;
|
||||||
|
uint32_t* dst = picture->argb + top * stride + left;
|
||||||
|
for (j = 0; j < height; ++j, dst += stride) {
|
||||||
|
for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
|
||||||
|
if (rect != NULL) {
|
||||||
|
ClearRectangle(pic, rect->x_offset, rect->y_offset,
|
||||||
|
rect->width, rect->height);
|
||||||
|
} else {
|
||||||
|
ClearRectangle(pic, 0, 0, pic->width, pic->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
|
||||||
|
WebPCopyPixels(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||||
|
const WebPPicture* const prev_canvas,
|
||||||
|
WebPPicture* const curr_canvas) {
|
||||||
|
assert(rect != NULL);
|
||||||
|
if (dispose == GIF_DISPOSE_BACKGROUND) {
|
||||||
|
GIFClearPic(curr_canvas, rect);
|
||||||
|
} else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
|
||||||
|
const size_t src_stride = prev_canvas->argb_stride;
|
||||||
|
const uint32_t* const src = prev_canvas->argb + rect->x_offset
|
||||||
|
+ rect->y_offset * src_stride;
|
||||||
|
const size_t dst_stride = curr_canvas->argb_stride;
|
||||||
|
uint32_t* const dst = curr_canvas->argb + rect->x_offset
|
||||||
|
+ rect->y_offset * dst_stride;
|
||||||
|
assert(prev_canvas != NULL);
|
||||||
|
WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
|
||||||
|
(uint8_t*)dst, (int)(4 * dst_stride),
|
||||||
|
4 * rect->width, rect->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFBlendFrames(const WebPPicture* const src,
|
||||||
|
const GIFFrameRect* const rect, WebPPicture* const dst) {
|
||||||
|
int i, j;
|
||||||
|
const size_t src_stride = src->argb_stride;
|
||||||
|
const size_t dst_stride = dst->argb_stride;
|
||||||
|
assert(src->width == dst->width && src->height == dst->height);
|
||||||
|
for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
|
||||||
|
for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
|
||||||
|
const uint32_t src_pixel = src->argb[j * src_stride + i];
|
||||||
|
const int src_alpha = src_pixel >> 24;
|
||||||
|
if (src_alpha != 0) {
|
||||||
|
dst->argb[j * dst_stride + i] = src_pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFDisplayError(const GifFileType* const gif, int gif_error) {
|
||||||
|
// libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
|
||||||
|
#if LOCAL_GIF_PREREQ(4,2)
|
||||||
|
#if LOCAL_GIF_PREREQ(5,0)
|
||||||
|
// Static string actually, hence the const char* cast.
|
||||||
|
const char* error_str = (const char*)GifErrorString(
|
||||||
|
(gif == NULL) ? gif_error : gif->Error);
|
||||||
|
#else
|
||||||
|
const char* error_str = (const char*)GifErrorString();
|
||||||
|
(void)gif;
|
||||||
|
#endif
|
||||||
|
if (error_str == NULL) error_str = "Unknown error";
|
||||||
|
fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
|
||||||
|
#else
|
||||||
|
(void)gif;
|
||||||
|
fprintf(stderr, "GIFLib Error %d: ", gif_error);
|
||||||
|
PrintGifError();
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !WEBP_HAVE_GIF
|
||||||
|
|
||||||
|
static void ErrorGIFNotAvailable() {
|
||||||
|
fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
|
||||||
|
"package before building.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
|
||||||
|
int bgcolor_index, int transparent_index,
|
||||||
|
uint32_t* const bgcolor) {
|
||||||
|
(void)color_map;
|
||||||
|
(void)bgcolor_index;
|
||||||
|
(void)transparent_index;
|
||||||
|
(void)bgcolor;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
|
||||||
|
GIFDisposeMethod* const dispose,
|
||||||
|
int* const transparent_index) {
|
||||||
|
(void)data;
|
||||||
|
(void)duration;
|
||||||
|
(void)dispose;
|
||||||
|
(void)transparent_index;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
|
||||||
|
GIFFrameRect* const gif_rect,
|
||||||
|
struct WebPPicture* const picture) {
|
||||||
|
(void)gif;
|
||||||
|
(void)transparent_index;
|
||||||
|
(void)gif_rect;
|
||||||
|
(void)picture;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
|
||||||
|
int* const loop_count) {
|
||||||
|
(void)gif;
|
||||||
|
(void)buf;
|
||||||
|
(void)loop_count;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
|
||||||
|
struct WebPData* const metadata) {
|
||||||
|
(void)gif;
|
||||||
|
(void)buf;
|
||||||
|
(void)metadata;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||||
|
const struct WebPPicture* const prev_canvas,
|
||||||
|
struct WebPPicture* const curr_canvas) {
|
||||||
|
(void)dispose;
|
||||||
|
(void)rect;
|
||||||
|
(void)prev_canvas;
|
||||||
|
(void)curr_canvas;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFBlendFrames(const struct WebPPicture* const src,
|
||||||
|
const GIFFrameRect* const rect,
|
||||||
|
struct WebPPicture* const dst) {
|
||||||
|
(void)src;
|
||||||
|
(void)rect;
|
||||||
|
(void)dst;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
|
||||||
|
(void)gif;
|
||||||
|
(void)gif_error;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFClearPic(struct WebPPicture* const pic,
|
||||||
|
const GIFFrameRect* const rect) {
|
||||||
|
(void)pic;
|
||||||
|
(void)rect;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GIFCopyPixels(const struct WebPPicture* const src,
|
||||||
|
struct WebPPicture* const dst) {
|
||||||
|
(void)src;
|
||||||
|
(void)dst;
|
||||||
|
ErrorGIFNotAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WEBP_HAVE_GIF
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
116
examples/gifdec.h
Normal file
116
examples/gifdec.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GIF decode.
|
||||||
|
|
||||||
|
#ifndef WEBP_EXAMPLES_GIFDEC_H_
|
||||||
|
#define WEBP_EXAMPLES_GIFDEC_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WEBP_HAVE_GIF
|
||||||
|
#include <gif_lib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
|
||||||
|
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
|
||||||
|
# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
|
||||||
|
# define LOCAL_GIF_PREREQ(maj, min) \
|
||||||
|
(LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
|
||||||
|
#else
|
||||||
|
# define LOCAL_GIF_VERSION 0
|
||||||
|
# define LOCAL_GIF_PREREQ(maj, min) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GIF_INDEX_INVALID (-1)
|
||||||
|
|
||||||
|
typedef enum GIFDisposeMethod {
|
||||||
|
GIF_DISPOSE_NONE,
|
||||||
|
GIF_DISPOSE_BACKGROUND,
|
||||||
|
GIF_DISPOSE_RESTORE_PREVIOUS
|
||||||
|
} GIFDisposeMethod;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x_offset, y_offset, width, height;
|
||||||
|
} GIFFrameRect;
|
||||||
|
|
||||||
|
struct WebPData;
|
||||||
|
struct WebPPicture;
|
||||||
|
|
||||||
|
#ifndef WEBP_HAVE_GIF
|
||||||
|
struct ColorMapObject;
|
||||||
|
struct GifFileType;
|
||||||
|
typedef unsigned char GifByteType;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Given the index of background color and transparent color, returns the
|
||||||
|
// corresponding background color (in BGRA format) in 'bgcolor'.
|
||||||
|
void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
|
||||||
|
int bgcolor_index, int transparent_index,
|
||||||
|
uint32_t* const bgcolor);
|
||||||
|
|
||||||
|
// Parses the given graphics extension data to get frame duration (in 1ms
|
||||||
|
// units), dispose method and transparent color index.
|
||||||
|
// Returns true on success.
|
||||||
|
int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
|
||||||
|
GIFDisposeMethod* const dispose,
|
||||||
|
int* const transparent_index);
|
||||||
|
|
||||||
|
// Reads the next GIF frame from 'gif' into 'picture'. Also, returns the GIF
|
||||||
|
// frame dimensions and offsets in 'rect'.
|
||||||
|
// Returns true on success.
|
||||||
|
int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
|
||||||
|
GIFFrameRect* const gif_rect,
|
||||||
|
struct WebPPicture* const picture);
|
||||||
|
|
||||||
|
// Parses loop count from the given Netscape extension data.
|
||||||
|
int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
|
||||||
|
int* const loop_count);
|
||||||
|
|
||||||
|
// Parses the given ICC or XMP extension data and stores it into 'metadata'.
|
||||||
|
// Returns true on success.
|
||||||
|
int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
|
||||||
|
struct WebPData* const metadata);
|
||||||
|
|
||||||
|
// Dispose the pixels within 'rect' of 'curr_canvas' based on 'dispose' method
|
||||||
|
// and 'prev_canvas'.
|
||||||
|
void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
|
||||||
|
const struct WebPPicture* const prev_canvas,
|
||||||
|
struct WebPPicture* const curr_canvas);
|
||||||
|
|
||||||
|
// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
|
||||||
|
void GIFBlendFrames(const struct WebPPicture* const src,
|
||||||
|
const GIFFrameRect* const rect,
|
||||||
|
struct WebPPicture* const dst);
|
||||||
|
|
||||||
|
// Prints an error string based on 'gif_error'.
|
||||||
|
void GIFDisplayError(const struct GifFileType* const gif, int gif_error);
|
||||||
|
|
||||||
|
// In the given 'pic', clear the pixels in 'rect' to transparent color.
|
||||||
|
void GIFClearPic(struct WebPPicture* const pic, const GIFFrameRect* const rect);
|
||||||
|
|
||||||
|
// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
|
||||||
|
// to be already allocated.
|
||||||
|
void GIFCopyPixels(const struct WebPPicture* const src,
|
||||||
|
struct WebPPicture* const dst);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_EXAMPLES_GIFDEC_H_
|
||||||
300
examples/img2webp.c
Normal file
300
examples/img2webp.c
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// generate an animated WebP out of a sequence of images
|
||||||
|
// (PNG, JPEG, ...)
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
// img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
|
||||||
|
//
|
||||||
|
// Author: skal@google.com (Pascal Massimino)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../examples/example_util.h"
|
||||||
|
#include "../imageio/image_dec.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
#include "./stopwatch.h"
|
||||||
|
#include "webp/encode.h"
|
||||||
|
#include "webp/mux.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void Help(void) {
|
||||||
|
printf("Usage:\n\n");
|
||||||
|
printf(" img2webp [file-level options] [image files...] "
|
||||||
|
"[per-frame options...]\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("File-level options (only used at the start of compression):\n");
|
||||||
|
printf(" -min_size ............ minimize size\n");
|
||||||
|
printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
|
||||||
|
printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
|
||||||
|
" (0=only keyframes)\n");
|
||||||
|
printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
|
||||||
|
" (0=disable key-frames altogether)\n");
|
||||||
|
printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
|
||||||
|
printf(" -v ................... verbose mode\n");
|
||||||
|
printf(" -h ................... this help\n");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Per-frame options (only used for subsequent images input):\n");
|
||||||
|
printf(" -d <int> ............. frame duration in ms (default: 100)\n");
|
||||||
|
printf(" -lossless ........... use lossless mode (default)\n");
|
||||||
|
printf(" -lossy ... ........... use lossy mode\n");
|
||||||
|
printf(" -q <float> ........... quality\n");
|
||||||
|
printf(" -m <int> ............. method to use\n");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
|
||||||
|
" -d 80 in2.tiff -o out.webp\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int ReadImage(const char filename[], WebPPicture* const pic) {
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
|
WebPImageReader reader;
|
||||||
|
int ok;
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
// Try to decode the file using WIC falling back to the other readers for
|
||||||
|
// e.g., WebP.
|
||||||
|
ok = ReadPictureWithWIC(filename, pic, 1, NULL);
|
||||||
|
if (ok) return 1;
|
||||||
|
#endif
|
||||||
|
if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
|
||||||
|
reader = WebPGuessImageReader(data, data_size);
|
||||||
|
ok = reader(data, data_size, pic, 1, NULL);
|
||||||
|
free((void*)data);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int SetLoopCount(int loop_count, WebPData* const webp_data) {
|
||||||
|
int ok = 1;
|
||||||
|
WebPMuxError err;
|
||||||
|
uint32_t features;
|
||||||
|
WebPMuxAnimParams new_params;
|
||||||
|
WebPMux* const mux = WebPMuxCreate(webp_data, 1);
|
||||||
|
if (mux == NULL) return 0;
|
||||||
|
|
||||||
|
err = WebPMuxGetFeatures(mux, &features);
|
||||||
|
ok = (err == WEBP_MUX_OK);
|
||||||
|
if (!ok || !(features & ANIMATION_FLAG)) goto End;
|
||||||
|
|
||||||
|
err = WebPMuxGetAnimationParams(mux, &new_params);
|
||||||
|
ok = (err == WEBP_MUX_OK);
|
||||||
|
if (ok) {
|
||||||
|
new_params.loop_count = loop_count;
|
||||||
|
err = WebPMuxSetAnimationParams(mux, &new_params);
|
||||||
|
ok = (err == WEBP_MUX_OK);
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
WebPDataClear(webp_data);
|
||||||
|
err = WebPMuxAssemble(mux, webp_data);
|
||||||
|
ok = (err == WEBP_MUX_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
End:
|
||||||
|
WebPMuxDelete(mux);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Error during loop-count setting\n");
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
const char* output = NULL;
|
||||||
|
WebPAnimEncoder* enc = NULL;
|
||||||
|
int verbose = 0;
|
||||||
|
int pic_num = 0;
|
||||||
|
int duration = 100;
|
||||||
|
int timestamp_ms = 0;
|
||||||
|
int ok = 1;
|
||||||
|
int loop_count = 0;
|
||||||
|
int width = 0, height = 0;
|
||||||
|
WebPAnimEncoderOptions anim_config;
|
||||||
|
WebPConfig config;
|
||||||
|
WebPPicture pic;
|
||||||
|
WebPData webp_data;
|
||||||
|
int c;
|
||||||
|
int have_input = 0;
|
||||||
|
|
||||||
|
WebPDataInit(&webp_data);
|
||||||
|
if (!WebPAnimEncoderOptionsInit(&anim_config) ||
|
||||||
|
!WebPConfigInit(&config) ||
|
||||||
|
!WebPPictureInit(&pic)) {
|
||||||
|
fprintf(stderr, "Library version mismatch!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1st pass of option parsing
|
||||||
|
for (c = 1; ok && c < argc; ++c) {
|
||||||
|
if (argv[c][0] == '-') {
|
||||||
|
int parse_error = 0;
|
||||||
|
if (!strcmp(argv[c], "-o") && c + 1 < argc) {
|
||||||
|
argv[c] = NULL;
|
||||||
|
output = argv[++c];
|
||||||
|
} else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
|
||||||
|
argv[c] = NULL;
|
||||||
|
anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
|
||||||
|
argv[c] = NULL;
|
||||||
|
anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
|
||||||
|
argv[c] = NULL;
|
||||||
|
loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
if (loop_count < 0) {
|
||||||
|
fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(argv[c], "-min_size")) {
|
||||||
|
anim_config.minimize_size = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-mixed")) {
|
||||||
|
anim_config.allow_mixed = 1;
|
||||||
|
config.lossless = 0;
|
||||||
|
} else if (!strcmp(argv[c], "-v")) {
|
||||||
|
verbose = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||||
|
Help();
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ok = !parse_error;
|
||||||
|
if (!ok) goto End;
|
||||||
|
argv[c] = NULL; // mark option as 'parsed' during 1st pass
|
||||||
|
} else {
|
||||||
|
have_input |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!have_input) {
|
||||||
|
fprintf(stderr, "No input file(s) for generating animation!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// image-reading pass
|
||||||
|
pic_num = 0;
|
||||||
|
config.lossless = 1;
|
||||||
|
for (c = 1; ok && c < argc; ++c) {
|
||||||
|
if (argv[c] == NULL) continue;
|
||||||
|
if (argv[c][0] == '-') { // parse local options
|
||||||
|
int parse_error = 0;
|
||||||
|
if (!strcmp(argv[c], "-lossy")) {
|
||||||
|
if (!anim_config.allow_mixed) config.lossless = 0;
|
||||||
|
} else if (!strcmp(argv[c], "-lossless")) {
|
||||||
|
if (!anim_config.allow_mixed) config.lossless = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
|
||||||
|
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
|
||||||
|
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
} else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
|
||||||
|
duration = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
|
if (duration <= 0) {
|
||||||
|
fprintf(stderr, "Invalid negative duration (%d)\n", duration);
|
||||||
|
parse_error = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parse_error = 1; // shouldn't be here.
|
||||||
|
fprintf(stderr, "Unknown option [%s]\n", argv[c]);
|
||||||
|
}
|
||||||
|
ok = !parse_error;
|
||||||
|
if (!ok) goto End;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
ok = WebPValidateConfig(&config);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Invalid configuration.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read next input image
|
||||||
|
pic.use_argb = 1;
|
||||||
|
ok = ReadImage(argv[c], &pic);
|
||||||
|
if (!ok) goto End;
|
||||||
|
|
||||||
|
if (enc == NULL) {
|
||||||
|
width = pic.width;
|
||||||
|
height = pic.height;
|
||||||
|
enc = WebPAnimEncoderNew(width, height, &anim_config);
|
||||||
|
ok = (enc != NULL);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
ok = (width == pic.width && height == pic.height);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Frame #%d dimension mismatched! "
|
||||||
|
"Got %d x %d. Was expecting %d x %d.\n",
|
||||||
|
pic_num, pic.width, pic.height, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Error while adding frame #%d\n", pic_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebPPictureFree(&pic);
|
||||||
|
if (!ok) goto End;
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Added frame #%3d at time %4d (file: %s)\n",
|
||||||
|
pic_num, timestamp_ms, argv[c]);
|
||||||
|
}
|
||||||
|
timestamp_ms += duration;
|
||||||
|
++pic_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a last fake frame to signal the last duration
|
||||||
|
ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
|
||||||
|
ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Error during final animation assembly.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
End:
|
||||||
|
// free resources
|
||||||
|
WebPAnimEncoderDelete(enc);
|
||||||
|
|
||||||
|
if (ok && loop_count > 0) { // Re-mux to add loop count.
|
||||||
|
ok = SetLoopCount(loop_count, &webp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
if (output != NULL) {
|
||||||
|
ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
|
||||||
|
if (ok) fprintf(stderr, "output file: %s ", output);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[no output file specified] ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
fprintf(stderr, "[%d frames, %u bytes].\n",
|
||||||
|
pic_num, (unsigned int)webp_data.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPDataClear(&webp_data);
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
#ifndef WEBP_EXAMPLES_STOPWATCH_H_
|
#ifndef WEBP_EXAMPLES_STOPWATCH_H_
|
||||||
#define WEBP_EXAMPLES_STOPWATCH_H_
|
#define WEBP_EXAMPLES_STOPWATCH_H_
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
#if defined _WIN32 && !defined __GNUC__
|
#if defined _WIN32 && !defined __GNUC__
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
|
|||||||
|
|
||||||
|
|
||||||
#else /* !_WIN32 */
|
#else /* !_WIN32 */
|
||||||
|
#include <string.h> // memcpy
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
typedef struct timeval Stopwatch;
|
typedef struct timeval Stopwatch;
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
#include "webp/config.h"
|
#include "webp/config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__unix__) || defined(__CYGWIN__)
|
||||||
|
#define _POSIX_C_SOURCE 200112L // for setenv
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -36,14 +40,13 @@
|
|||||||
#include "webp/decode.h"
|
#include "webp/decode.h"
|
||||||
#include "webp/demux.h"
|
#include "webp/demux.h"
|
||||||
|
|
||||||
#include "./example_util.h"
|
#include "../examples/example_util.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void Help(void);
|
|
||||||
|
|
||||||
// Unfortunate global variables. Gathered into a struct for comfort.
|
// Unfortunate global variables. Gathered into a struct for comfort.
|
||||||
static struct {
|
static struct {
|
||||||
int has_animation;
|
int has_animation;
|
||||||
@@ -51,6 +54,7 @@ static struct {
|
|||||||
int done;
|
int done;
|
||||||
int decoding_error;
|
int decoding_error;
|
||||||
int print_info;
|
int print_info;
|
||||||
|
int only_deltas;
|
||||||
int use_color_profile;
|
int use_color_profile;
|
||||||
|
|
||||||
int canvas_width, canvas_height;
|
int canvas_width, canvas_height;
|
||||||
@@ -65,6 +69,7 @@ static struct {
|
|||||||
WebPIterator curr_frame;
|
WebPIterator curr_frame;
|
||||||
WebPIterator prev_frame;
|
WebPIterator prev_frame;
|
||||||
WebPChunkIterator iccp;
|
WebPChunkIterator iccp;
|
||||||
|
int viewport_width, viewport_height;
|
||||||
} kParams;
|
} kParams;
|
||||||
|
|
||||||
static void ClearPreviousPic(void) {
|
static void ClearPreviousPic(void) {
|
||||||
@@ -82,6 +87,16 @@ static void ClearParams(void) {
|
|||||||
kParams.dmux = NULL;
|
kParams.dmux = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the previous frame to the dimensions of the canvas and has it dispose
|
||||||
|
// to background to cause the canvas to be cleared.
|
||||||
|
static void ClearPreviousFrame(void) {
|
||||||
|
WebPIterator* const prev = &kParams.prev_frame;
|
||||||
|
prev->width = kParams.canvas_width;
|
||||||
|
prev->height = kParams.canvas_height;
|
||||||
|
prev->x_offset = prev->y_offset = 0;
|
||||||
|
prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Color profile handling
|
// Color profile handling
|
||||||
static int ApplyColorProfile(const WebPData* const profile,
|
static int ApplyColorProfile(const WebPData* const profile,
|
||||||
@@ -181,6 +196,8 @@ static void decode_callback(int what) {
|
|||||||
if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
|
if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
|
||||||
--kParams.loop_count;
|
--kParams.loop_count;
|
||||||
kParams.done = (kParams.loop_count == 0);
|
kParams.done = (kParams.loop_count == 0);
|
||||||
|
if (kParams.done) return;
|
||||||
|
ClearPreviousFrame();
|
||||||
} else {
|
} else {
|
||||||
kParams.decoding_error = 1;
|
kParams.decoding_error = 1;
|
||||||
kParams.done = 1;
|
kParams.done = 1;
|
||||||
@@ -232,18 +249,27 @@ static void HandleKey(unsigned char key, int pos_x, int pos_y) {
|
|||||||
}
|
}
|
||||||
} else if (key == 'i') {
|
} else if (key == 'i') {
|
||||||
kParams.print_info = 1 - kParams.print_info;
|
kParams.print_info = 1 - kParams.print_info;
|
||||||
|
// TODO(skal): handle refresh of animation's last-frame too. It's quite
|
||||||
|
// more involved though (need to save the previous frame).
|
||||||
|
if (!kParams.has_animation) ClearPreviousFrame();
|
||||||
|
glutPostRedisplay();
|
||||||
|
} else if (key == 'd') {
|
||||||
|
kParams.only_deltas = 1 - kParams.only_deltas;
|
||||||
glutPostRedisplay();
|
glutPostRedisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HandleReshape(int width, int height) {
|
static void HandleReshape(int width, int height) {
|
||||||
// TODO(skal): proper handling of resize, esp. for large pictures.
|
// TODO(skal): should we preserve aspect ratio?
|
||||||
// + key control of the zoom.
|
// Also: handle larger-than-screen pictures correctly.
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
kParams.viewport_width = width;
|
||||||
|
kParams.viewport_height = height;
|
||||||
|
if (!kParams.has_animation) ClearPreviousFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintString(const char* const text) {
|
static void PrintString(const char* const text) {
|
||||||
@@ -285,32 +311,42 @@ static void HandleDisplay(void) {
|
|||||||
GLfloat xoff, yoff;
|
GLfloat xoff, yoff;
|
||||||
if (pic == NULL) return;
|
if (pic == NULL) return;
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glPixelZoom(1, -1);
|
glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
|
||||||
|
(GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
|
||||||
xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
|
xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
|
||||||
yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
|
yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
|
||||||
glRasterPos2f(-1.f + xoff, 1.f - yoff);
|
glRasterPos2f(-1.f + xoff, 1.f - yoff);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
|
||||||
|
|
||||||
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
|
if (kParams.only_deltas) {
|
||||||
|
DrawCheckerBoard();
|
||||||
|
} else if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
|
||||||
curr->blend_method == WEBP_MUX_NO_BLEND) {
|
curr->blend_method == WEBP_MUX_NO_BLEND) {
|
||||||
// TODO(later): these offsets and those above should factor in window size.
|
|
||||||
// they will be incorrect if the window is resized.
|
|
||||||
// glScissor() takes window coordinates (0,0 at bottom left).
|
// glScissor() takes window coordinates (0,0 at bottom left).
|
||||||
int window_x, window_y;
|
int window_x, window_y;
|
||||||
|
int frame_w, frame_h;
|
||||||
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||||
// Clear the previous frame rectangle.
|
// Clear the previous frame rectangle.
|
||||||
window_x = prev->x_offset;
|
window_x = prev->x_offset;
|
||||||
window_y = kParams.canvas_height - prev->y_offset - prev->height;
|
window_y = kParams.canvas_height - prev->y_offset - prev->height;
|
||||||
|
frame_w = prev->width;
|
||||||
|
frame_h = prev->height;
|
||||||
} else { // curr->blend_method == WEBP_MUX_NO_BLEND.
|
} else { // curr->blend_method == WEBP_MUX_NO_BLEND.
|
||||||
// We simulate no-blending behavior by first clearing the current frame
|
// We simulate no-blending behavior by first clearing the current frame
|
||||||
// rectangle (to a checker-board) and then alpha-blending against it.
|
// rectangle (to a checker-board) and then alpha-blending against it.
|
||||||
window_x = curr->x_offset;
|
window_x = curr->x_offset;
|
||||||
window_y = kParams.canvas_height - curr->y_offset - curr->height;
|
window_y = kParams.canvas_height - curr->y_offset - curr->height;
|
||||||
|
frame_w = curr->width;
|
||||||
|
frame_h = curr->height;
|
||||||
}
|
}
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
// Only update the requested area, not the whole canvas.
|
// Only update the requested area, not the whole canvas.
|
||||||
glScissor(window_x, window_y, prev->width, prev->height);
|
window_x = window_x * kParams.viewport_width / kParams.canvas_width;
|
||||||
|
window_y = window_y * kParams.viewport_height / kParams.canvas_height;
|
||||||
|
frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
|
||||||
|
frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
|
||||||
|
glScissor(window_x, window_y, frame_w, frame_h);
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT); // use clear color
|
glClear(GL_COLOR_BUFFER_BIT); // use clear color
|
||||||
DrawCheckerBoard();
|
DrawCheckerBoard();
|
||||||
@@ -352,6 +388,7 @@ static void StartDisplay(void) {
|
|||||||
glutInitWindowSize(width, height);
|
glutInitWindowSize(width, height);
|
||||||
glutCreateWindow("WebP viewer");
|
glutCreateWindow("WebP viewer");
|
||||||
glutDisplayFunc(HandleDisplay);
|
glutDisplayFunc(HandleDisplay);
|
||||||
|
glutReshapeFunc(HandleReshape);
|
||||||
glutIdleFunc(NULL);
|
glutIdleFunc(NULL);
|
||||||
glutKeyboardFunc(HandleKey);
|
glutKeyboardFunc(HandleKey);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
@@ -360,7 +397,6 @@ static void StartDisplay(void) {
|
|||||||
GetColorf(kParams.bg_color, 8),
|
GetColorf(kParams.bg_color, 8),
|
||||||
GetColorf(kParams.bg_color, 16),
|
GetColorf(kParams.bg_color, 16),
|
||||||
GetColorf(kParams.bg_color, 24));
|
GetColorf(kParams.bg_color, 24));
|
||||||
HandleReshape(width, height);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
DrawCheckerBoard();
|
DrawCheckerBoard();
|
||||||
}
|
}
|
||||||
@@ -372,21 +408,20 @@ static void Help(void) {
|
|||||||
printf("Usage: vwebp in_file [options]\n\n"
|
printf("Usage: vwebp in_file [options]\n\n"
|
||||||
"Decodes the WebP image file and visualize it using OpenGL\n"
|
"Decodes the WebP image file and visualize it using OpenGL\n"
|
||||||
"Options are:\n"
|
"Options are:\n"
|
||||||
" -version .... print version number and exit\n"
|
" -version ..... print version number and exit\n"
|
||||||
" -noicc ....... don't use the icc profile if present\n"
|
" -noicc ....... don't use the icc profile if present\n"
|
||||||
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
" -nofancy ..... don't use the fancy YUV420 upscaler\n"
|
||||||
" -nofilter .... disable in-loop filtering\n"
|
" -nofilter .... disable in-loop filtering\n"
|
||||||
" -dither <int> dithering strength (0..100), default=50\n"
|
" -dither <int> dithering strength (0..100), default=50\n"
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
" -noalphadither disable alpha plane dithering\n"
|
" -noalphadither disable alpha plane dithering\n"
|
||||||
#endif
|
|
||||||
" -mt .......... use multi-threading\n"
|
" -mt .......... use multi-threading\n"
|
||||||
" -info ........ print info\n"
|
" -info ........ print info\n"
|
||||||
" -h ....... this help message\n"
|
" -h ........... this help message\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Keyboard shortcuts:\n"
|
"Keyboard shortcuts:\n"
|
||||||
" 'c' ................ toggle use of color profile\n"
|
" 'c' ................ toggle use of color profile\n"
|
||||||
" 'i' ................ overlay file information\n"
|
" 'i' ................ overlay file information\n"
|
||||||
|
" 'd' ................ disable blending & disposal (debug)\n"
|
||||||
" 'q' / 'Q' / ESC .... quit\n"
|
" 'q' / 'Q' / ESC .... quit\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -395,19 +430,17 @@ int main(int argc, char *argv[]) {
|
|||||||
int c;
|
int c;
|
||||||
WebPDecoderConfig* const config = &kParams.config;
|
WebPDecoderConfig* const config = &kParams.config;
|
||||||
WebPIterator* const curr = &kParams.curr_frame;
|
WebPIterator* const curr = &kParams.curr_frame;
|
||||||
WebPIterator* const prev = &kParams.prev_frame;
|
|
||||||
|
|
||||||
if (!WebPInitDecoderConfig(config)) {
|
if (!WebPInitDecoderConfig(config)) {
|
||||||
fprintf(stderr, "Library version mismatch!\n");
|
fprintf(stderr, "Library version mismatch!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
config->options.dithering_strength = 50;
|
config->options.dithering_strength = 50;
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
config->options.alpha_dithering_strength = 100;
|
config->options.alpha_dithering_strength = 100;
|
||||||
#endif
|
|
||||||
kParams.use_color_profile = 1;
|
kParams.use_color_profile = 1;
|
||||||
|
|
||||||
for (c = 1; c < argc; ++c) {
|
for (c = 1; c < argc; ++c) {
|
||||||
|
int parse_error = 0;
|
||||||
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
|
||||||
Help();
|
Help();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -417,12 +450,11 @@ int main(int argc, char *argv[]) {
|
|||||||
config->options.no_fancy_upsampling = 1;
|
config->options.no_fancy_upsampling = 1;
|
||||||
} else if (!strcmp(argv[c], "-nofilter")) {
|
} else if (!strcmp(argv[c], "-nofilter")) {
|
||||||
config->options.bypass_filtering = 1;
|
config->options.bypass_filtering = 1;
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
} else if (!strcmp(argv[c], "-noalphadither")) {
|
} else if (!strcmp(argv[c], "-noalphadither")) {
|
||||||
config->options.alpha_dithering_strength = 0;
|
config->options.alpha_dithering_strength = 0;
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
|
} else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
|
||||||
config->options.dithering_strength = strtol(argv[++c], NULL, 0);
|
config->options.dithering_strength =
|
||||||
|
ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||||
} else if (!strcmp(argv[c], "-info")) {
|
} else if (!strcmp(argv[c], "-info")) {
|
||||||
kParams.print_info = 1;
|
kParams.print_info = 1;
|
||||||
} else if (!strcmp(argv[c], "-version")) {
|
} else if (!strcmp(argv[c], "-version")) {
|
||||||
@@ -445,6 +477,11 @@ int main(int argc, char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
kParams.file_name = argv[c];
|
kParams.file_name = argv[c];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_error) {
|
||||||
|
Help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kParams.file_name == NULL) {
|
if (kParams.file_name == NULL) {
|
||||||
@@ -453,7 +490,7 @@ int main(int argc, char *argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ExUtilReadFile(kParams.file_name,
|
if (!ImgIoUtilReadFile(kParams.file_name,
|
||||||
&kParams.data.bytes, &kParams.data.size)) {
|
&kParams.data.bytes, &kParams.data.size)) {
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
@@ -469,20 +506,13 @@ int main(int argc, char *argv[]) {
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) {
|
|
||||||
fprintf(stderr, "Image fragments are not supported for now!\n");
|
|
||||||
goto Error;
|
|
||||||
}
|
|
||||||
kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
|
kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
|
||||||
kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
|
kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
|
||||||
if (kParams.print_info) {
|
if (kParams.print_info) {
|
||||||
printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
|
printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev->width = kParams.canvas_width;
|
ClearPreviousFrame();
|
||||||
prev->height = kParams.canvas_height;
|
|
||||||
prev->x_offset = prev->y_offset = 0;
|
|
||||||
prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
|
|
||||||
|
|
||||||
memset(&kParams.iccp, 0, sizeof(kParams.iccp));
|
memset(&kParams.iccp, 0, sizeof(kParams.iccp));
|
||||||
kParams.has_color_profile =
|
kParams.has_color_profile =
|
||||||
@@ -514,6 +544,12 @@ int main(int argc, char *argv[]) {
|
|||||||
WebPDemuxGetFrame(kParams.dmux, 0, curr);
|
WebPDemuxGetFrame(kParams.dmux, 0, curr);
|
||||||
if (kParams.loop_count) ++kParams.loop_count;
|
if (kParams.loop_count) ++kParams.loop_count;
|
||||||
|
|
||||||
|
#if defined(__unix__) || defined(__CYGWIN__)
|
||||||
|
// Work around GLUT compositor bug.
|
||||||
|
// https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
|
||||||
|
setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Start display (and timer)
|
// Start display (and timer)
|
||||||
glutInit(&argc, argv);
|
glutInit(&argc, argv);
|
||||||
#ifdef FREEGLUT
|
#ifdef FREEGLUT
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
// Copyright 2014 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 decode.
|
|
||||||
|
|
||||||
#include "./webpdec.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "webp/decode.h"
|
|
||||||
#include "webp/encode.h"
|
|
||||||
#include "./example_util.h"
|
|
||||||
#include "./metadata.h"
|
|
||||||
|
|
||||||
int ReadWebP(const char* const in_file, WebPPicture* const pic,
|
|
||||||
int keep_alpha, Metadata* const metadata) {
|
|
||||||
int ok = 0;
|
|
||||||
size_t data_size = 0;
|
|
||||||
const uint8_t* data = NULL;
|
|
||||||
VP8StatusCode status = VP8_STATUS_OK;
|
|
||||||
WebPDecoderConfig config;
|
|
||||||
WebPDecBuffer* const output_buffer = &config.output;
|
|
||||||
WebPBitstreamFeatures* const bitstream = &config.input;
|
|
||||||
|
|
||||||
// TODO(jzern): add Exif/XMP/ICC extraction.
|
|
||||||
if (metadata != NULL) {
|
|
||||||
fprintf(stderr, "Warning: metadata extraction from WebP is unsupported.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WebPInitDecoderConfig(&config)) {
|
|
||||||
fprintf(stderr, "Library version mismatch!\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ExUtilLoadWebP(in_file, &data, &data_size, bitstream)) {
|
|
||||||
const int has_alpha = keep_alpha && bitstream->has_alpha;
|
|
||||||
output_buffer->colorspace = has_alpha ? MODE_RGBA : MODE_RGB;
|
|
||||||
|
|
||||||
status = ExUtilDecodeWebP(data, data_size, 0, &config);
|
|
||||||
if (status == VP8_STATUS_OK) {
|
|
||||||
const uint8_t* const rgba = output_buffer->u.RGBA.rgba;
|
|
||||||
const int stride = output_buffer->u.RGBA.stride;
|
|
||||||
pic->width = output_buffer->width;
|
|
||||||
pic->height = output_buffer->height;
|
|
||||||
pic->use_argb = 1;
|
|
||||||
ok = has_alpha ? WebPPictureImportRGBA(pic, rgba, stride)
|
|
||||||
: WebPPictureImportRGB(pic, rgba, stride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != VP8_STATUS_OK) {
|
|
||||||
ExUtilPrintWebPError(in_file, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
free((void*)data);
|
|
||||||
WebPFreeDecBuffer(output_buffer);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Copyright 2014 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 decode.
|
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_WEBPDEC_H_
|
|
||||||
#define WEBP_EXAMPLES_WEBPDEC_H_
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Metadata;
|
|
||||||
struct WebPPicture;
|
|
||||||
|
|
||||||
// Reads a WebP from 'in_file', returning the decoded output in 'pic'.
|
|
||||||
// If 'keep_alpha' is true and the WebP has an alpha channel, the output is
|
|
||||||
// RGBA otherwise it will be RGB.
|
|
||||||
// Returns true on success.
|
|
||||||
int ReadWebP(const char* const in_file, struct WebPPicture* const pic,
|
|
||||||
int keep_alpha, struct Metadata* const metadata);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_WEBPDEC_H_
|
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
|
webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
|
||||||
|
|
||||||
Extract relevant data from WebP container file:
|
Extract relevant data from WebP container file:
|
||||||
webpmux -get frgm n in.webp -o out_fragment.webp
|
|
||||||
webpmux -get frame n in.webp -o out_frame.webp
|
webpmux -get frame n in.webp -o out_frame.webp
|
||||||
webpmux -get icc in.webp -o image_profile.icc
|
webpmux -get icc in.webp -o image_profile.icc
|
||||||
webpmux -get exif in.webp -o image_metadata.exif
|
webpmux -get exif in.webp -o image_metadata.exif
|
||||||
@@ -39,6 +38,11 @@
|
|||||||
webpmux -strip exif in.webp -o out.webp
|
webpmux -strip exif in.webp -o out.webp
|
||||||
webpmux -strip xmp in.webp -o out.webp
|
webpmux -strip xmp in.webp -o out.webp
|
||||||
|
|
||||||
|
Change duration of frame intervals:
|
||||||
|
webpmux -duration 150 in.webp -o out.webp
|
||||||
|
webpmux -duration 33,2 in.webp -o out.webp
|
||||||
|
webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
|
||||||
|
|
||||||
Misc:
|
Misc:
|
||||||
webpmux -info in.webp
|
webpmux -info in.webp
|
||||||
webpmux [ -h | -help ]
|
webpmux [ -h | -help ]
|
||||||
@@ -55,7 +59,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "webp/decode.h"
|
#include "webp/decode.h"
|
||||||
#include "webp/mux.h"
|
#include "webp/mux.h"
|
||||||
#include "./example_util.h"
|
#include "../examples/example_util.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Config object to parse command-line arguments.
|
// Config object to parse command-line arguments.
|
||||||
@@ -66,7 +71,8 @@ typedef enum {
|
|||||||
ACTION_SET,
|
ACTION_SET,
|
||||||
ACTION_STRIP,
|
ACTION_STRIP,
|
||||||
ACTION_INFO,
|
ACTION_INFO,
|
||||||
ACTION_HELP
|
ACTION_HELP,
|
||||||
|
ACTION_DURATION
|
||||||
} ActionType;
|
} ActionType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -88,17 +94,17 @@ typedef enum {
|
|||||||
FEATURE_XMP,
|
FEATURE_XMP,
|
||||||
FEATURE_ICCP,
|
FEATURE_ICCP,
|
||||||
FEATURE_ANMF,
|
FEATURE_ANMF,
|
||||||
FEATURE_FRGM,
|
FEATURE_DURATION,
|
||||||
LAST_FEATURE
|
LAST_FEATURE
|
||||||
} FeatureType;
|
} FeatureType;
|
||||||
|
|
||||||
static const char* const kFourccList[LAST_FEATURE] = {
|
static const char* const kFourccList[LAST_FEATURE] = {
|
||||||
NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM"
|
NULL, "EXIF", "XMP ", "ICCP", "ANMF"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* const kDescriptions[LAST_FEATURE] = {
|
static const char* const kDescriptions[LAST_FEATURE] = {
|
||||||
NULL, "EXIF metadata", "XMP metadata", "ICC profile",
|
NULL, "EXIF metadata", "XMP metadata", "ICC profile",
|
||||||
"Animation frame", "Image fragment"
|
"Animation frame"
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -182,39 +188,32 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
printf("Canvas size: %d x %d\n", width, height);
|
printf("Canvas size: %d x %d\n", width, height);
|
||||||
|
|
||||||
err = WebPMuxGetFeatures(mux, &flag);
|
err = WebPMuxGetFeatures(mux, &flag);
|
||||||
#ifndef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
|
|
||||||
#endif
|
|
||||||
RETURN_IF_ERROR("Failed to retrieve features\n");
|
RETURN_IF_ERROR("Failed to retrieve features\n");
|
||||||
|
|
||||||
if (flag == 0) {
|
if (flag == 0) {
|
||||||
fprintf(stderr, "No features present.\n");
|
printf("No features present.\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the features present.
|
// Print the features present.
|
||||||
printf("Features present:");
|
printf("Features present:");
|
||||||
if (flag & ANIMATION_FLAG) printf(" animation");
|
if (flag & ANIMATION_FLAG) printf(" animation");
|
||||||
if (flag & FRAGMENTS_FLAG) printf(" image fragments");
|
|
||||||
if (flag & ICCP_FLAG) printf(" ICC profile");
|
if (flag & ICCP_FLAG) printf(" ICC profile");
|
||||||
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
if (flag & EXIF_FLAG) printf(" EXIF metadata");
|
||||||
if (flag & XMP_FLAG) printf(" XMP metadata");
|
if (flag & XMP_FLAG) printf(" XMP metadata");
|
||||||
if (flag & ALPHA_FLAG) printf(" transparency");
|
if (flag & ALPHA_FLAG) printf(" transparency");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
|
if (flag & ANIMATION_FLAG) {
|
||||||
const int is_anim = !!(flag & ANIMATION_FLAG);
|
const WebPChunkId id = WEBP_CHUNK_ANMF;
|
||||||
const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
|
const char* const type_str = "frame";
|
||||||
const char* const type_str = is_anim ? "frame" : "fragment";
|
|
||||||
int nFrames;
|
int nFrames;
|
||||||
|
|
||||||
if (is_anim) {
|
|
||||||
WebPMuxAnimParams params;
|
WebPMuxAnimParams params;
|
||||||
err = WebPMuxGetAnimationParams(mux, ¶ms);
|
err = WebPMuxGetAnimationParams(mux, ¶ms);
|
||||||
assert(err == WEBP_MUX_OK);
|
assert(err == WEBP_MUX_OK);
|
||||||
printf("Background color : 0x%.8X Loop Count : %d\n",
|
printf("Background color : 0x%.8X Loop Count : %d\n",
|
||||||
params.bgcolor, params.loop_count);
|
params.bgcolor, params.loop_count);
|
||||||
}
|
|
||||||
|
|
||||||
err = WebPMuxNumChunks(mux, id, &nFrames);
|
err = WebPMuxNumChunks(mux, id, &nFrames);
|
||||||
assert(err == WEBP_MUX_OK);
|
assert(err == WEBP_MUX_OK);
|
||||||
@@ -223,8 +222,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
if (nFrames > 0) {
|
if (nFrames > 0) {
|
||||||
int i;
|
int i;
|
||||||
printf("No.: width height alpha x_offset y_offset ");
|
printf("No.: width height alpha x_offset y_offset ");
|
||||||
if (is_anim) printf("duration dispose blend ");
|
printf("duration dispose blend ");
|
||||||
printf("image_size\n");
|
printf("image_size compression\n");
|
||||||
for (i = 1; i <= nFrames; i++) {
|
for (i = 1; i <= nFrames; i++) {
|
||||||
WebPMuxFrameInfo frame;
|
WebPMuxFrameInfo frame;
|
||||||
err = WebPMuxGetFrame(mux, i, &frame);
|
err = WebPMuxGetFrame(mux, i, &frame);
|
||||||
@@ -237,7 +236,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
|
printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
|
||||||
features.height, features.has_alpha ? "yes" : "no",
|
features.height, features.has_alpha ? "yes" : "no",
|
||||||
frame.x_offset, frame.y_offset);
|
frame.x_offset, frame.y_offset);
|
||||||
if (is_anim) {
|
{
|
||||||
const char* const dispose =
|
const char* const dispose =
|
||||||
(frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
|
(frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
|
||||||
: "background";
|
: "background";
|
||||||
@@ -245,7 +244,10 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
(frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
|
(frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
|
||||||
printf("%8d %10s %5s ", frame.duration, dispose, blend);
|
printf("%8d %10s %5s ", frame.duration, dispose, blend);
|
||||||
}
|
}
|
||||||
printf("%10d\n", (int)frame.bitstream.size);
|
printf("%10d %11s\n", (int)frame.bitstream.size,
|
||||||
|
(features.format == 1) ? "lossy" :
|
||||||
|
(features.format == 2) ? "lossless" :
|
||||||
|
"undefined");
|
||||||
}
|
}
|
||||||
WebPDataClear(&frame.bitstream);
|
WebPDataClear(&frame.bitstream);
|
||||||
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
||||||
@@ -274,7 +276,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
printf("Size of the XMP metadata: %d\n", (int)xmp.size);
|
printf("Size of the XMP metadata: %d\n", (int)xmp.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
|
if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
|
||||||
WebPMuxFrameInfo image;
|
WebPMuxFrameInfo image;
|
||||||
err = WebPMuxGetFrame(mux, 1, &image);
|
err = WebPMuxGetFrame(mux, 1, &image);
|
||||||
if (err == WEBP_MUX_OK) {
|
if (err == WEBP_MUX_OK) {
|
||||||
@@ -290,10 +292,9 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
static void PrintHelp(void) {
|
static void PrintHelp(void) {
|
||||||
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
|
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
|
||||||
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
|
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
|
||||||
|
printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
|
||||||
|
printf(" INPUT -o OUTPUT\n");
|
||||||
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
|
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
|
|
||||||
#endif
|
|
||||||
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||||
"\n");
|
"\n");
|
||||||
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
|
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
|
||||||
@@ -307,9 +308,6 @@ static void PrintHelp(void) {
|
|||||||
printf(" icc get ICC profile\n");
|
printf(" icc get ICC profile\n");
|
||||||
printf(" exif get EXIF metadata\n");
|
printf(" exif get EXIF metadata\n");
|
||||||
printf(" xmp get XMP metadata\n");
|
printf(" xmp get XMP metadata\n");
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
printf(" frgm n get nth fragment\n");
|
|
||||||
#endif
|
|
||||||
printf(" frame n get nth frame\n");
|
printf(" frame n get nth frame\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -322,6 +320,18 @@ static void PrintHelp(void) {
|
|||||||
printf(" 'file.exif' contains the EXIF metadata to be set\n");
|
printf(" 'file.exif' contains the EXIF metadata to be set\n");
|
||||||
printf(" 'file.xmp' contains the XMP metadata to be set\n");
|
printf(" 'file.xmp' contains the XMP metadata to be set\n");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("DURATION_OPTIONS:\n");
|
||||||
|
printf(" Set duration of selected frames:\n");
|
||||||
|
printf(" duration set duration for each frames\n");
|
||||||
|
printf(" duration,frame set duration of a particular frame\n");
|
||||||
|
printf(" duration,start,end set duration of frames in the\n");
|
||||||
|
printf(" interval [start,end])\n");
|
||||||
|
printf(" where: 'duration' is the duration in milliseconds\n");
|
||||||
|
printf(" 'start' is the start frame index\n");
|
||||||
|
printf(" 'end' is the inclusive end frame index\n");
|
||||||
|
printf(" The special 'end' value '0' means: last frame.\n");
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("STRIP_OPTIONS:\n");
|
printf("STRIP_OPTIONS:\n");
|
||||||
printf(" Strip color profile/metadata:\n");
|
printf(" Strip color profile/metadata:\n");
|
||||||
@@ -329,16 +339,6 @@ static void PrintHelp(void) {
|
|||||||
printf(" exif strip EXIF metadata\n");
|
printf(" exif strip EXIF metadata\n");
|
||||||
printf(" xmp strip XMP metadata\n");
|
printf(" xmp strip XMP metadata\n");
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
printf("\n");
|
|
||||||
printf("FRAGMENT_OPTIONS(i):\n");
|
|
||||||
printf(" Create fragmented image:\n");
|
|
||||||
printf(" file_i +xi+yi\n");
|
|
||||||
printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
|
|
||||||
printf(" 'xi','yi' specify the image offset for this fragment"
|
|
||||||
"\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("FRAME_OPTIONS(i):\n");
|
printf("FRAME_OPTIONS(i):\n");
|
||||||
printf(" Create animation:\n");
|
printf(" Create animation:\n");
|
||||||
@@ -383,7 +383,7 @@ static int ReadFileToWebPData(const char* const filename,
|
|||||||
WebPData* const webp_data) {
|
WebPData* const webp_data) {
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
if (!ExUtilReadFile(filename, &data, &size)) return 0;
|
if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
|
||||||
webp_data->bytes = data;
|
webp_data->bytes = data;
|
||||||
webp_data->size = size;
|
webp_data->size = size;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -402,8 +402,9 @@ static int CreateMux(const char* const filename, WebPMux** mux) {
|
|||||||
|
|
||||||
static int WriteData(const char* filename, const WebPData* const webpdata) {
|
static int WriteData(const char* filename, const WebPData* const webpdata) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
|
FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb")
|
||||||
if (!fout) {
|
: ImgIoUtilSetBinaryMode(stdout);
|
||||||
|
if (fout == NULL) {
|
||||||
fprintf(stderr, "Error opening output WebP file %s!\n", filename);
|
fprintf(stderr, "Error opening output WebP file %s!\n", filename);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -431,6 +432,45 @@ static int WriteWebP(WebPMux* const mux, const char* filename) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
|
||||||
|
WebPMux* new_mux = WebPMuxNew();
|
||||||
|
WebPMuxAnimParams p;
|
||||||
|
WebPMuxError err;
|
||||||
|
int i;
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
if (new_mux == NULL) return NULL;
|
||||||
|
|
||||||
|
err = WebPMuxGetAnimationParams(mux, &p);
|
||||||
|
if (err == WEBP_MUX_OK) {
|
||||||
|
err = WebPMuxSetAnimationParams(new_mux, &p);
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
ERROR_GOTO2("Error (%s) handling animation params.\n",
|
||||||
|
ErrorString(err), End);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* it might not be an animation. Just keep moving. */
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= 3; ++i) {
|
||||||
|
WebPData metadata;
|
||||||
|
err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
|
||||||
|
if (err == WEBP_MUX_OK && metadata.size > 0) {
|
||||||
|
err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
End:
|
||||||
|
if (!ok) {
|
||||||
|
WebPMuxDelete(new_mux);
|
||||||
|
new_mux = NULL;
|
||||||
|
}
|
||||||
|
return new_mux;
|
||||||
|
}
|
||||||
|
|
||||||
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
||||||
int dispose_method, dummy;
|
int dispose_method, dummy;
|
||||||
char plus_minus, blend_method;
|
char plus_minus, blend_method;
|
||||||
@@ -466,13 +506,6 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
|
|
||||||
const int ok =
|
|
||||||
(sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
|
|
||||||
if (ok) WarnAboutOddOffset(info);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
||||||
uint32_t a, r, g, b;
|
uint32_t a, r, g, b;
|
||||||
if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
|
if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
|
||||||
@@ -487,7 +520,7 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
|||||||
static void DeleteConfig(WebPMuxConfig* config) {
|
static void DeleteConfig(WebPMuxConfig* config) {
|
||||||
if (config != NULL) {
|
if (config != NULL) {
|
||||||
free(config->feature_.args_);
|
free(config->feature_.args_);
|
||||||
free(config);
|
memset(config, 0, sizeof(*config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,9 +534,9 @@ static void DeleteConfig(WebPMuxConfig* config) {
|
|||||||
static int ValidateCommandLine(int argc, const char* argv[],
|
static int ValidateCommandLine(int argc, const char* argv[],
|
||||||
int* num_feature_args) {
|
int* num_feature_args) {
|
||||||
int num_frame_args;
|
int num_frame_args;
|
||||||
int num_frgm_args;
|
|
||||||
int num_loop_args;
|
int num_loop_args;
|
||||||
int num_bgcolor_args;
|
int num_bgcolor_args;
|
||||||
|
int num_durations_args;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
|
|
||||||
assert(num_feature_args != NULL);
|
assert(num_feature_args != NULL);
|
||||||
@@ -528,9 +561,9 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
|
|
||||||
// Compound checks.
|
// Compound checks.
|
||||||
num_frame_args = CountOccurrences(argv, argc, "-frame");
|
num_frame_args = CountOccurrences(argv, argc, "-frame");
|
||||||
num_frgm_args = CountOccurrences(argv, argc, "-frgm");
|
|
||||||
num_loop_args = CountOccurrences(argv, argc, "-loop");
|
num_loop_args = CountOccurrences(argv, argc, "-loop");
|
||||||
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
|
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
|
||||||
|
num_durations_args = CountOccurrences(argv, argc, "-duration");
|
||||||
|
|
||||||
if (num_loop_args > 1) {
|
if (num_loop_args > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
||||||
@@ -543,22 +576,20 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
|
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
|
||||||
"case of animation.\n", ErrValidate);
|
"case of animation.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (num_frame_args > 0 && num_frgm_args > 0) {
|
if (num_durations_args > 0 && num_frame_args != 0) {
|
||||||
ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
|
ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
|
||||||
"time.\n", ErrValidate);
|
ErrValidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ok == 1);
|
assert(ok == 1);
|
||||||
if (num_frame_args == 0 && num_frgm_args == 0) {
|
if (num_durations_args > 0) {
|
||||||
|
*num_feature_args = num_durations_args;
|
||||||
|
} else if (num_frame_args == 0) {
|
||||||
// Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
|
// Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
|
||||||
*num_feature_args = 1;
|
*num_feature_args = 1;
|
||||||
} else {
|
} else {
|
||||||
// Multiple arguments ('set' action for animation or fragmented image).
|
// Multiple arguments ('set' action for animation)
|
||||||
if (num_frame_args > 0) {
|
|
||||||
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
|
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
|
||||||
} else {
|
|
||||||
*num_feature_args = num_frgm_args;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrValidate:
|
ErrValidate:
|
||||||
@@ -600,6 +631,21 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
|
} else if (!strcmp(argv[i], "-duration")) {
|
||||||
|
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
||||||
|
if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
|
||||||
|
config->action_type_ = ACTION_DURATION;
|
||||||
|
} else {
|
||||||
|
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
||||||
|
}
|
||||||
|
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_DURATION) {
|
||||||
|
feature->type_ = FEATURE_DURATION;
|
||||||
|
} else {
|
||||||
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
|
}
|
||||||
|
arg->params_ = argv[i + 1];
|
||||||
|
++feature_arg_index;
|
||||||
|
i += 2;
|
||||||
} else if (!strcmp(argv[i], "-get")) {
|
} else if (!strcmp(argv[i], "-get")) {
|
||||||
if (ACTION_IS_NIL) {
|
if (ACTION_IS_NIL) {
|
||||||
config->action_type_ = ACTION_GET;
|
config->action_type_ = ACTION_GET;
|
||||||
@@ -649,24 +695,6 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
arg->params_ = argv[i + 1];
|
arg->params_ = argv[i + 1];
|
||||||
++feature_arg_index;
|
++feature_arg_index;
|
||||||
i += 2;
|
i += 2;
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
} else if (!strcmp(argv[i], "-frgm")) {
|
|
||||||
CHECK_NUM_ARGS_LESS(3, ErrParse);
|
|
||||||
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
|
|
||||||
config->action_type_ = ACTION_SET;
|
|
||||||
} else {
|
|
||||||
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
|
|
||||||
}
|
|
||||||
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
|
|
||||||
feature->type_ = FEATURE_FRGM;
|
|
||||||
} else {
|
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
|
||||||
}
|
|
||||||
arg->filename_ = argv[i + 1];
|
|
||||||
arg->params_ = argv[i + 2];
|
|
||||||
++feature_arg_index;
|
|
||||||
i += 3;
|
|
||||||
#endif
|
|
||||||
} else if (!strcmp(argv[i], "-o")) {
|
} else if (!strcmp(argv[i], "-o")) {
|
||||||
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
||||||
config->output_ = argv[i + 1];
|
config->output_ = argv[i + 1];
|
||||||
@@ -726,16 +754,10 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
} else if ((!strcmp(argv[i], "frame") ||
|
|
||||||
!strcmp(argv[i], "frgm")) &&
|
|
||||||
#else
|
|
||||||
} else if (!strcmp(argv[i], "frame") &&
|
} else if (!strcmp(argv[i], "frame") &&
|
||||||
#endif
|
|
||||||
(config->action_type_ == ACTION_GET)) {
|
(config->action_type_ == ACTION_GET)) {
|
||||||
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
||||||
feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
|
feature->type_ = FEATURE_ANMF;
|
||||||
FEATURE_FRGM;
|
|
||||||
arg->params_ = argv[i + 1];
|
arg->params_ = argv[i + 1];
|
||||||
++feature_arg_index;
|
++feature_arg_index;
|
||||||
i += 2;
|
i += 2;
|
||||||
@@ -773,8 +795,7 @@ static int ValidateConfig(WebPMuxConfig* config) {
|
|||||||
if (config->input_ == NULL) {
|
if (config->input_ == NULL) {
|
||||||
if (config->action_type_ != ACTION_SET) {
|
if (config->action_type_ != ACTION_SET) {
|
||||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||||
} else if (feature->type_ != FEATURE_ANMF &&
|
} else if (feature->type_ != FEATURE_ANMF) {
|
||||||
feature->type_ != FEATURE_FRGM) {
|
|
||||||
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -790,33 +811,27 @@ static int ValidateConfig(WebPMuxConfig* config) {
|
|||||||
|
|
||||||
// Create config object from command-line arguments.
|
// Create config object from command-line arguments.
|
||||||
static int InitializeConfig(int argc, const char* argv[],
|
static int InitializeConfig(int argc, const char* argv[],
|
||||||
WebPMuxConfig** config) {
|
WebPMuxConfig* config) {
|
||||||
int num_feature_args = 0;
|
int num_feature_args = 0;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
|
|
||||||
assert(config != NULL);
|
assert(config != NULL);
|
||||||
*config = NULL;
|
memset(config, 0, sizeof(*config));
|
||||||
|
|
||||||
// Validate command-line arguments.
|
// Validate command-line arguments.
|
||||||
if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
|
if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
|
||||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate memory.
|
config->feature_.arg_count_ = num_feature_args;
|
||||||
*config = (WebPMuxConfig*)calloc(1, sizeof(**config));
|
config->feature_.args_ =
|
||||||
if (*config == NULL) {
|
(FeatureArg*)calloc(num_feature_args, sizeof(*config->feature_.args_));
|
||||||
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
if (config->feature_.args_ == NULL) {
|
||||||
}
|
|
||||||
(*config)->feature_.arg_count_ = num_feature_args;
|
|
||||||
(*config)->feature_.args_ =
|
|
||||||
(FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
|
|
||||||
if ((*config)->feature_.args_ == NULL) {
|
|
||||||
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command-line.
|
// Parse command-line.
|
||||||
if (!ParseCommandLine(argc, argv, *config) ||
|
if (!ParseCommandLine(argc, argv, config) || !ValidateConfig(config)) {
|
||||||
!ValidateConfig(*config)) {
|
|
||||||
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,25 +847,26 @@ static int InitializeConfig(int argc, const char* argv[],
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Processing.
|
// Processing.
|
||||||
|
|
||||||
static int GetFrameFragment(const WebPMux* mux,
|
static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) {
|
||||||
const WebPMuxConfig* config, int is_frame) {
|
|
||||||
WebPMuxError err = WEBP_MUX_OK;
|
WebPMuxError err = WEBP_MUX_OK;
|
||||||
WebPMux* mux_single = NULL;
|
WebPMux* mux_single = NULL;
|
||||||
long num = 0;
|
int num = 0;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
|
int parse_error = 0;
|
||||||
|
const WebPChunkId id = WEBP_CHUNK_ANMF;
|
||||||
WebPMuxFrameInfo info;
|
WebPMuxFrameInfo info;
|
||||||
WebPDataInit(&info.bitstream);
|
WebPDataInit(&info.bitstream);
|
||||||
|
|
||||||
num = strtol(config->feature_.args_[0].params_, NULL, 10);
|
num = ExUtilGetInt(config->feature_.args_[0].params_, 10, &parse_error);
|
||||||
if (num < 0) {
|
if (num < 0) {
|
||||||
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
|
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
|
||||||
}
|
}
|
||||||
|
if (parse_error) goto ErrGet;
|
||||||
|
|
||||||
err = WebPMuxGetFrame(mux, num, &info);
|
err = WebPMuxGetFrame(mux, num, &info);
|
||||||
if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
|
if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n",
|
ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
|
||||||
ErrorString(err), num, ErrGet);
|
ErrorString(err), num, ErrGet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +887,7 @@ static int GetFrameFragment(const WebPMux* mux,
|
|||||||
ErrGet:
|
ErrGet:
|
||||||
WebPDataClear(&info.bitstream);
|
WebPDataClear(&info.bitstream);
|
||||||
WebPMuxDelete(mux_single);
|
WebPMuxDelete(mux_single);
|
||||||
return ok;
|
return ok && !parse_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and process config.
|
// Read and process config.
|
||||||
@@ -888,9 +904,7 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
switch (feature->type_) {
|
switch (feature->type_) {
|
||||||
case FEATURE_ANMF:
|
case FEATURE_ANMF:
|
||||||
case FEATURE_FRGM:
|
ok = GetFrame(mux, config);
|
||||||
ok = GetFrameFragment(mux, config,
|
|
||||||
(feature->type_ == FEATURE_ANMF) ? 1 : 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FEATURE_ICCP:
|
case FEATURE_ICCP:
|
||||||
@@ -933,16 +947,19 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SUBTYPE_LOOP: {
|
case SUBTYPE_LOOP: {
|
||||||
const long loop_count =
|
int parse_error = 0;
|
||||||
strtol(feature->args_[i].params_, NULL, 10);
|
const int loop_count =
|
||||||
if (loop_count != (int)loop_count) {
|
ExUtilGetInt(feature->args_[i].params_, 10, &parse_error);
|
||||||
|
if (loop_count < 0 || loop_count > 65535) {
|
||||||
// Note: This is only a 'necessary' condition for loop_count
|
// Note: This is only a 'necessary' condition for loop_count
|
||||||
// to be valid. The 'sufficient' conditioned in checked in
|
// to be valid. The 'sufficient' conditioned in checked in
|
||||||
// WebPMuxSetAnimationParams() method called later.
|
// WebPMuxSetAnimationParams() method called later.
|
||||||
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
|
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
|
||||||
"65535.\n", Err2);
|
"65535.\n", Err2);
|
||||||
}
|
}
|
||||||
params.loop_count = (int)loop_count;
|
ok = !parse_error;
|
||||||
|
if (!ok) goto Err2;
|
||||||
|
params.loop_count = loop_count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SUBTYPE_ANMF: {
|
case SUBTYPE_ANMF: {
|
||||||
@@ -979,35 +996,6 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FEATURE_FRGM: {
|
|
||||||
int i;
|
|
||||||
mux = WebPMuxNew();
|
|
||||||
if (mux == NULL) {
|
|
||||||
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
|
||||||
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
|
||||||
}
|
|
||||||
for (i = 0; i < feature->arg_count_; ++i) {
|
|
||||||
WebPMuxFrameInfo frgm;
|
|
||||||
frgm.id = WEBP_CHUNK_FRGM;
|
|
||||||
ok = ReadFileToWebPData(feature->args_[i].filename_,
|
|
||||||
&frgm.bitstream);
|
|
||||||
if (!ok) goto Err2;
|
|
||||||
ok = ParseFragmentArgs(feature->args_[i].params_, &frgm);
|
|
||||||
if (!ok) {
|
|
||||||
WebPDataClear(&frgm.bitstream);
|
|
||||||
ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
|
|
||||||
Err2);
|
|
||||||
}
|
|
||||||
err = WebPMuxPushFrame(mux, &frgm, 1);
|
|
||||||
WebPDataClear(&frgm.bitstream);
|
|
||||||
if (err != WEBP_MUX_OK) {
|
|
||||||
ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
|
|
||||||
ErrorString(err), i, Err2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case FEATURE_ICCP:
|
case FEATURE_ICCP:
|
||||||
case FEATURE_EXIF:
|
case FEATURE_EXIF:
|
||||||
case FEATURE_XMP: {
|
case FEATURE_XMP: {
|
||||||
@@ -1031,6 +1019,89 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
ok = WriteWebP(mux, config->output_);
|
ok = WriteWebP(mux, config->output_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ACTION_DURATION: {
|
||||||
|
int num_frames;
|
||||||
|
ok = CreateMux(config->input_, &mux);
|
||||||
|
if (!ok) goto Err2;
|
||||||
|
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
|
||||||
|
ok = (err == WEBP_MUX_OK);
|
||||||
|
if (!ok) {
|
||||||
|
ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
|
||||||
|
}
|
||||||
|
if (num_frames == 0) {
|
||||||
|
fprintf(stderr, "Doesn't look like the source is animated. "
|
||||||
|
"Skipping duration setting.\n");
|
||||||
|
ok = WriteWebP(mux, config->output_);
|
||||||
|
if (!ok) goto Err2;
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
int* durations = NULL;
|
||||||
|
WebPMux* new_mux = DuplicateMuxHeader(mux);
|
||||||
|
if (new_mux == NULL) goto Err2;
|
||||||
|
durations = (int*)malloc((size_t)num_frames * sizeof(*durations));
|
||||||
|
if (durations == NULL) goto Err2;
|
||||||
|
for (i = 0; i < num_frames; ++i) durations[i] = -1;
|
||||||
|
|
||||||
|
// Parse intervals to process.
|
||||||
|
for (i = 0; i < feature->arg_count_; ++i) {
|
||||||
|
int k;
|
||||||
|
int args[3];
|
||||||
|
int duration, start, end;
|
||||||
|
const int nb_args = ExUtilGetInts(feature->args_[i].params_,
|
||||||
|
10, 3, args);
|
||||||
|
ok = (nb_args >= 1);
|
||||||
|
if (!ok) goto Err3;
|
||||||
|
duration = args[0];
|
||||||
|
if (duration < 0) {
|
||||||
|
ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb_args == 1) { // only duration is present -> use full interval
|
||||||
|
start = 1;
|
||||||
|
end = num_frames;
|
||||||
|
} else {
|
||||||
|
start = args[1];
|
||||||
|
if (start <= 0) {
|
||||||
|
start = 1;
|
||||||
|
} else if (start > num_frames) {
|
||||||
|
start = num_frames;
|
||||||
|
}
|
||||||
|
end = (nb_args >= 3) ? args[2] : start;
|
||||||
|
if (end == 0 || end > num_frames) end = num_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k = start; k <= end; ++k) {
|
||||||
|
assert(k >= 1 && k <= num_frames);
|
||||||
|
durations[k - 1] = duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply non-negative durations to their destination frames.
|
||||||
|
for (i = 1; i <= num_frames; ++i) {
|
||||||
|
WebPMuxFrameInfo frame;
|
||||||
|
err = WebPMuxGetFrame(mux, i, &frame);
|
||||||
|
if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
|
||||||
|
ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
|
||||||
|
}
|
||||||
|
if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
|
||||||
|
err = WebPMuxPushFrame(new_mux, &frame, 1);
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
|
||||||
|
}
|
||||||
|
WebPDataClear(&frame.bitstream);
|
||||||
|
}
|
||||||
|
WebPMuxDelete(mux);
|
||||||
|
ok = WriteWebP(new_mux, config->output_);
|
||||||
|
mux = new_mux; // transfer for the WebPMuxDelete() call
|
||||||
|
new_mux = NULL;
|
||||||
|
|
||||||
|
Err3:
|
||||||
|
free(durations);
|
||||||
|
WebPMuxDelete(new_mux);
|
||||||
|
if (!ok) goto Err2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ACTION_STRIP: {
|
case ACTION_STRIP: {
|
||||||
ok = CreateMux(config->input_, &mux);
|
ok = CreateMux(config->input_, &mux);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
@@ -1069,14 +1140,14 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
// Main.
|
// Main.
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
WebPMuxConfig* config;
|
WebPMuxConfig config;
|
||||||
int ok = InitializeConfig(argc - 1, argv + 1, &config);
|
int ok = InitializeConfig(argc - 1, argv + 1, &config);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
ok = Process(config);
|
ok = Process(&config);
|
||||||
} else {
|
} else {
|
||||||
PrintHelp();
|
PrintHelp();
|
||||||
}
|
}
|
||||||
DeleteConfig(config);
|
DeleteConfig(&config);
|
||||||
return !ok;
|
return !ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
extras/Makefile.am
Normal file
26
extras/Makefile.am
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||||
|
noinst_LTLIBRARIES = libwebpextras.la
|
||||||
|
|
||||||
|
noinst_HEADERS =
|
||||||
|
noinst_HEADERS += ../src/webp/types.h
|
||||||
|
|
||||||
|
libwebpextras_la_SOURCES =
|
||||||
|
libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c
|
||||||
|
|
||||||
|
libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
libwebpextras_la_LDFLAGS = -lm
|
||||||
|
libwebpextras_la_LIBADD = ../src/libwebp.la
|
||||||
|
|
||||||
|
noinst_PROGRAMS = get_disto webp_quality
|
||||||
|
|
||||||
|
get_disto_SOURCES = get_disto.c
|
||||||
|
get_disto_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
get_disto_LDADD = ../imageio/libimageio_util.la ../imageio/libimagedec.la
|
||||||
|
get_disto_LDADD += ../src/libwebp.la
|
||||||
|
get_disto_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
|
||||||
|
|
||||||
|
webp_quality_SOURCES = webp_quality.c
|
||||||
|
webp_quality_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
|
webp_quality_LDADD = ../imageio/libimageio_util.la
|
||||||
|
webp_quality_LDADD += libwebpextras.la
|
||||||
|
webp_quality_LDADD += ../src/libwebp.la
|
||||||
142
extras/extras.c
Normal file
142
extras/extras.c
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Additional WebP utilities.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "./extras.h"
|
||||||
|
#include "webp/format_constants.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define XTRA_MAJ_VERSION 0
|
||||||
|
#define XTRA_MIN_VERSION 1
|
||||||
|
#define XTRA_REV_VERSION 0
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int WebPGetExtrasVersion(void) {
|
||||||
|
return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
|
||||||
|
int y, width, uv_width;
|
||||||
|
if (pic == NULL || gray_data == NULL) return 0;
|
||||||
|
pic->colorspace = WEBP_YUV420;
|
||||||
|
if (!WebPPictureAlloc(pic)) return 0;
|
||||||
|
width = pic->width;
|
||||||
|
uv_width = (width + 1) >> 1;
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
memcpy(pic->y + y * pic->y_stride, gray_data, width);
|
||||||
|
gray_data += width; // <- we could use some 'data_stride' here if needed
|
||||||
|
if ((y & 1) == 0) {
|
||||||
|
memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||||
|
memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) {
|
||||||
|
int x, y;
|
||||||
|
if (pic == NULL || rgb565 == NULL) return 0;
|
||||||
|
pic->colorspace = WEBP_YUV420;
|
||||||
|
pic->use_argb = 1;
|
||||||
|
if (!WebPPictureAlloc(pic)) return 0;
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
const int width = pic->width;
|
||||||
|
uint32_t* dst = pic->argb + y * pic->argb_stride;
|
||||||
|
for (x = 0; x < width; ++x) {
|
||||||
|
#ifdef WEBP_SWAP_16BIT_CSP
|
||||||
|
const uint32_t rg = rgb565[2 * x + 1];
|
||||||
|
const uint32_t gb = rgb565[2 * x + 0];
|
||||||
|
#else
|
||||||
|
const uint32_t rg = rgb565[2 * x + 0];
|
||||||
|
const uint32_t gb = rgb565[2 * x + 1];
|
||||||
|
#endif
|
||||||
|
uint32_t r = rg & 0xf8;
|
||||||
|
uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc;
|
||||||
|
uint32_t b = (gb << 5);
|
||||||
|
// dithering
|
||||||
|
r = r | (r >> 5);
|
||||||
|
g = g | (g >> 6);
|
||||||
|
b = b | (b >> 5);
|
||||||
|
dst[x] = (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
rgb565 += 2 * width;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) {
|
||||||
|
int x, y;
|
||||||
|
if (pic == NULL || rgb4444 == NULL) return 0;
|
||||||
|
pic->colorspace = WEBP_YUV420;
|
||||||
|
pic->use_argb = 1;
|
||||||
|
if (!WebPPictureAlloc(pic)) return 0;
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
const int width = pic->width;
|
||||||
|
uint32_t* dst = pic->argb + y * pic->argb_stride;
|
||||||
|
for (x = 0; x < width; ++x) {
|
||||||
|
#ifdef WEBP_SWAP_16BIT_CSP
|
||||||
|
const uint32_t rg = rgb4444[2 * x + 1];
|
||||||
|
const uint32_t ba = rgb4444[2 * x + 0];
|
||||||
|
#else
|
||||||
|
const uint32_t rg = rgb4444[2 * x + 0];
|
||||||
|
const uint32_t ba = rgb4444[2 * x + 1];
|
||||||
|
#endif
|
||||||
|
uint32_t r = rg & 0xf0;
|
||||||
|
uint32_t g = (rg << 4);
|
||||||
|
uint32_t b = (ba & 0xf0);
|
||||||
|
uint32_t a = (ba << 4);
|
||||||
|
// dithering
|
||||||
|
r = r | (r >> 4);
|
||||||
|
g = g | (g >> 4);
|
||||||
|
b = b | (b >> 4);
|
||||||
|
a = a | (a >> 4);
|
||||||
|
dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
rgb4444 += 2 * width;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
|
||||||
|
const uint32_t palette[], int palette_size,
|
||||||
|
WebPPicture* pic) {
|
||||||
|
int x, y;
|
||||||
|
uint32_t* dst;
|
||||||
|
// 256 as the input buffer is uint8_t.
|
||||||
|
assert(MAX_PALETTE_SIZE <= 256);
|
||||||
|
if (pic == NULL || indexed == NULL || indexed_stride < pic->width ||
|
||||||
|
palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pic->use_argb = 1;
|
||||||
|
if (!WebPPictureAlloc(pic)) return 0;
|
||||||
|
dst = pic->argb;
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
for (x = 0; x < pic->width; ++x) {
|
||||||
|
// Make sure we are within the palette.
|
||||||
|
if (indexed[x] >= palette_size) {
|
||||||
|
WebPPictureFree(pic);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dst[x] = palette[indexed[x]];
|
||||||
|
}
|
||||||
|
indexed += indexed_stride;
|
||||||
|
dst += pic->argb_stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
70
extras/extras.h
Normal file
70
extras/extras.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 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_EXTRAS_EXTRAS_H_
|
||||||
|
#define WEBP_EXTRAS_EXTRAS_H_
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "webp/encode.h"
|
||||||
|
|
||||||
|
#define WEBP_EXTRAS_ABI_VERSION 0x0001 // 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);
|
||||||
|
|
||||||
|
// Import a color mapped image. The number of colors is less or equal to
|
||||||
|
// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
|
||||||
|
// will be discarded. Returns 'false' in case of error, or if indexed[] contains
|
||||||
|
// invalid indices.
|
||||||
|
WEBP_EXTERN(int)
|
||||||
|
WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
|
||||||
|
const uint32_t palette[], int palette_size,
|
||||||
|
WebPPicture* pic);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Parse a bitstream, search for VP8 (lossy) header and report a
|
||||||
|
// rough estimation of the quality factor used for compressing the bitstream.
|
||||||
|
// If the bitstream is in lossless format, the special value '101' is returned.
|
||||||
|
// Otherwise (lossy bitstream), the returned value is in the range [0..100].
|
||||||
|
// Any error (invalid bitstream, animated WebP, incomplete header, etc.)
|
||||||
|
// will return a value of -1.
|
||||||
|
WEBP_EXTERN(int) VP8EstimateQuality(const uint8_t* const data, size_t size);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WEBP_EXTRAS_EXTRAS_H_ */
|
||||||
343
extras/get_disto.c
Normal file
343
extras/get_disto.c
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Simple tool to load two webp/png/jpg/tiff files and compute PSNR/SSIM.
|
||||||
|
// This is mostly a wrapper around WebPPictureDistortion().
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
gcc -o get_disto get_disto.c -O3 -I../ -L../examples -L../imageio \
|
||||||
|
-lexample_util -limageio_util -limagedec -lwebp -L/opt/local/lib \
|
||||||
|
-lpng -lz -ljpeg -ltiff -lm -lpthread
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "webp/encode.h"
|
||||||
|
#include "../imageio/image_dec.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
|
||||||
|
static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
|
||||||
|
int keep_alpha) {
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
|
WebPImageReader reader = NULL;
|
||||||
|
int ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
|
if (!ok) goto End;
|
||||||
|
|
||||||
|
pic->use_argb = 1; // force ARGB
|
||||||
|
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
// Try to decode the file using WIC falling back to the other readers for
|
||||||
|
// e.g., WebP.
|
||||||
|
ok = ReadPictureWithWIC(filename, pic, keep_alpha, NULL);
|
||||||
|
if (ok) goto End;
|
||||||
|
#endif
|
||||||
|
reader = WebPGuessImageReader(data, data_size);
|
||||||
|
ok = reader(data, data_size, pic, keep_alpha, NULL);
|
||||||
|
|
||||||
|
End:
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
||||||
|
}
|
||||||
|
free((void*)data);
|
||||||
|
return ok ? data_size : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RescalePlane(uint8_t* plane, int width, int height,
|
||||||
|
int x_stride, int y_stride, int max) {
|
||||||
|
const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
|
||||||
|
int x, y;
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
uint8_t* const ptr = plane + y * y_stride;
|
||||||
|
for (x = 0; x < width * x_stride; x += x_stride) {
|
||||||
|
const uint32_t diff = (ptr[x] * factor + (1 << 15)) >> 16;
|
||||||
|
ptr[x] = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the max absolute difference.
|
||||||
|
static int DiffScaleChannel(uint8_t* src1, int stride1,
|
||||||
|
const uint8_t* src2, int stride2,
|
||||||
|
int x_stride, int w, int h, int do_scaling) {
|
||||||
|
int x, y;
|
||||||
|
int max = 0;
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
uint8_t* const ptr1 = src1 + y * stride1;
|
||||||
|
const uint8_t* const ptr2 = src2 + y * stride2;
|
||||||
|
for (x = 0; x < w * x_stride; x += x_stride) {
|
||||||
|
const int diff = abs(ptr1[x] - ptr2[x]);
|
||||||
|
if (diff > max) max = diff;
|
||||||
|
ptr1[x] = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// SSIM calculation. We re-implement these functions here, out of dsp/, to avoid
|
||||||
|
// breaking the library's hidden visibility. This code duplication avoids the
|
||||||
|
// bigger annoyance of having to open up internal details of libdsp...
|
||||||
|
|
||||||
|
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
|
||||||
|
|
||||||
|
// struct for accumulating statistical moments
|
||||||
|
typedef struct {
|
||||||
|
uint32_t w; // sum(w_i) : sum of weights
|
||||||
|
uint32_t xm, ym; // sum(w_i * x_i), sum(w_i * y_i)
|
||||||
|
uint32_t xxm, xym, yym; // sum(w_i * x_i * x_i), etc.
|
||||||
|
} DistoStats;
|
||||||
|
|
||||||
|
// hat-shaped filter. Sum of coefficients is equal to 16.
|
||||||
|
static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
|
||||||
|
|
||||||
|
static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
|
||||||
|
const uint32_t N = stats->w;
|
||||||
|
const uint32_t w2 = N * N;
|
||||||
|
const uint32_t C1 = 20 * w2;
|
||||||
|
const uint32_t C2 = 60 * w2;
|
||||||
|
const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
|
||||||
|
const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
|
||||||
|
const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
|
||||||
|
if (xmxm + ymym >= C3) {
|
||||||
|
const int64_t xmym = (int64_t)stats->xm * stats->ym;
|
||||||
|
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
|
||||||
|
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
|
||||||
|
const uint64_t syy = (uint64_t)stats->yym * N - ymym;
|
||||||
|
// we descale by 8 to prevent overflow during the fnum/fden multiply.
|
||||||
|
const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
|
||||||
|
const uint64_t den_S = (sxx + syy + C2) >> 8;
|
||||||
|
const uint64_t fnum = (2 * xmym + C1) * num_S;
|
||||||
|
const uint64_t fden = (xmxm + ymym + C1) * den_S;
|
||||||
|
const double r = (double)fnum / fden;
|
||||||
|
assert(r >= 0. && r <= 1.0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return 1.; // area is too dark to contribute meaningfully
|
||||||
|
}
|
||||||
|
|
||||||
|
static double SSIMGetClipped(const uint8_t* src1, int stride1,
|
||||||
|
const uint8_t* src2, int stride2,
|
||||||
|
int xo, int yo, int W, int H) {
|
||||||
|
DistoStats stats = { 0, 0, 0, 0, 0, 0 };
|
||||||
|
const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
|
||||||
|
const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
|
||||||
|
const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
|
||||||
|
const int xmax = (xo + SSIM_KERNEL > W - 1) ? W - 1 : xo + SSIM_KERNEL;
|
||||||
|
int x, y;
|
||||||
|
src1 += ymin * stride1;
|
||||||
|
src2 += ymin * stride2;
|
||||||
|
for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
|
||||||
|
for (x = xmin; x <= xmax; ++x) {
|
||||||
|
const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
|
||||||
|
* kWeight[SSIM_KERNEL + y - yo];
|
||||||
|
const uint32_t s1 = src1[x];
|
||||||
|
const uint32_t s2 = src2[x];
|
||||||
|
stats.w += w;
|
||||||
|
stats.xm += w * s1;
|
||||||
|
stats.ym += w * s2;
|
||||||
|
stats.xxm += w * s1 * s1;
|
||||||
|
stats.xym += w * s1 * s2;
|
||||||
|
stats.yym += w * s2 * s2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SSIMCalculation(&stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
|
||||||
|
static int SSIMScaleChannel(uint8_t* src1, int stride1,
|
||||||
|
const uint8_t* src2, int stride2,
|
||||||
|
int x_stride, int w, int h, int do_scaling) {
|
||||||
|
int x, y;
|
||||||
|
int max = 0;
|
||||||
|
uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
|
||||||
|
uint8_t* const plane2 = plane1 + w * h;
|
||||||
|
if (plane1 == NULL) return -1;
|
||||||
|
|
||||||
|
// extract plane
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
for (x = 0; x < w; ++x) {
|
||||||
|
plane1[x + y * w] = src1[x * x_stride + y * stride1];
|
||||||
|
plane2[x + y * w] = src2[x * x_stride + y * stride2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (y = 0; y < h; ++y) {
|
||||||
|
for (x = 0; x < w; ++x) {
|
||||||
|
const double ssim = SSIMGetClipped(plane1, w, plane2, w, x, y, w, h);
|
||||||
|
int diff = (int)(255 * (1. - ssim));
|
||||||
|
if (diff < 0) {
|
||||||
|
diff = 0;
|
||||||
|
} else if (diff > max) {
|
||||||
|
max = diff;
|
||||||
|
}
|
||||||
|
src1[x * x_stride + y * stride1] = (diff > 255) ? 255u : (uint8_t)diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(plane1);
|
||||||
|
|
||||||
|
if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an argb picture to luminance.
|
||||||
|
static void ConvertToGray(WebPPicture* const pic) {
|
||||||
|
int x, y;
|
||||||
|
assert(pic != NULL);
|
||||||
|
assert(pic->use_argb);
|
||||||
|
for (y = 0; y < pic->height; ++y) {
|
||||||
|
uint32_t* const row = &pic->argb[y * pic->argb_stride];
|
||||||
|
for (x = 0; x < pic->width; ++x) {
|
||||||
|
const uint32_t argb = row[x];
|
||||||
|
const uint32_t r = (argb >> 16) & 0xff;
|
||||||
|
const uint32_t g = (argb >> 8) & 0xff;
|
||||||
|
const uint32_t b = (argb >> 0) & 0xff;
|
||||||
|
// We use BT.709 for converting to luminance.
|
||||||
|
const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
|
||||||
|
row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Help(void) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: get_disto [-ssim][-psnr][-alpha] compressed.webp orig.webp\n"
|
||||||
|
" -ssim ..... print SSIM distortion\n"
|
||||||
|
" -psnr ..... print PSNR distortion (default)\n"
|
||||||
|
" -alpha .... preserve alpha plane\n"
|
||||||
|
" -h ........ this message\n"
|
||||||
|
" -o <file> . save the diff map as a WebP lossless file\n"
|
||||||
|
" -scale .... scale the difference map to fit [0..255] range\n"
|
||||||
|
" -gray ..... use grayscale for difference map (-scale)\n"
|
||||||
|
" Also handles PNG, JPG and TIFF files, in addition to WebP.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
WebPPicture pic1, pic2;
|
||||||
|
size_t size1 = 0, size2 = 0;
|
||||||
|
int ret = 1;
|
||||||
|
float disto[5];
|
||||||
|
int type = 0;
|
||||||
|
int c;
|
||||||
|
int help = 0;
|
||||||
|
int keep_alpha = 0;
|
||||||
|
int scale = 0;
|
||||||
|
int use_gray = 0;
|
||||||
|
const char* name1 = NULL;
|
||||||
|
const char* name2 = NULL;
|
||||||
|
const char* output = NULL;
|
||||||
|
|
||||||
|
if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
|
||||||
|
fprintf(stderr, "Can't init pictures\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (c = 1; c < argc; ++c) {
|
||||||
|
if (!strcmp(argv[c], "-ssim")) {
|
||||||
|
type = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-psnr")) {
|
||||||
|
type = 0;
|
||||||
|
} else if (!strcmp(argv[c], "-alpha")) {
|
||||||
|
keep_alpha = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-scale")) {
|
||||||
|
scale = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-gray")) {
|
||||||
|
use_gray = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-h")) {
|
||||||
|
help = 1;
|
||||||
|
ret = 0;
|
||||||
|
} else if (!strcmp(argv[c], "-o")) {
|
||||||
|
if (++c == argc) {
|
||||||
|
fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
output = argv[c];
|
||||||
|
} else if (name1 == NULL) {
|
||||||
|
name1 = argv[c];
|
||||||
|
} else {
|
||||||
|
name2 = argv[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (help || name1 == NULL || name2 == NULL) {
|
||||||
|
if (!help) {
|
||||||
|
fprintf(stderr, "Error: missing arguments.\n");
|
||||||
|
}
|
||||||
|
Help();
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
size1 = ReadPicture(name1, &pic1, 1);
|
||||||
|
size2 = ReadPicture(name1, &pic2, 1);
|
||||||
|
if (size1 == 0 || size2 == 0) goto End;
|
||||||
|
|
||||||
|
if (!keep_alpha) {
|
||||||
|
WebPBlendAlpha(&pic1, 0x00000000);
|
||||||
|
WebPBlendAlpha(&pic2, 0x00000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) {
|
||||||
|
fprintf(stderr, "Error while computing the distortion.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
printf("%u %.2f %.2f %.2f %.2f %.2f\n",
|
||||||
|
(unsigned int)size1,
|
||||||
|
disto[4], disto[0], disto[1], disto[2], disto[3]);
|
||||||
|
|
||||||
|
if (output != NULL) {
|
||||||
|
uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
|
if (pic1.use_argb != pic2.use_argb) {
|
||||||
|
fprintf(stderr, "Pictures are not in the same argb format. "
|
||||||
|
"Can't save the difference map.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
if (pic1.use_argb) {
|
||||||
|
int n;
|
||||||
|
fprintf(stderr, "max differences per channel: ");
|
||||||
|
for (n = 0; n < 3; ++n) { // skip the alpha channel
|
||||||
|
const int range = (type == 1) ?
|
||||||
|
SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||||
|
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||||
|
4, pic1.width, pic1.height, scale) :
|
||||||
|
DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
|
||||||
|
(const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
|
||||||
|
4, pic1.width, pic1.height, scale);
|
||||||
|
if (range < 0) fprintf(stderr, "\nError computing diff map\n");
|
||||||
|
fprintf(stderr, "[%d]", range);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
if (use_gray) ConvertToGray(&pic1);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Can only compute the difference map in ARGB format.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
|
||||||
|
pic1.width, pic1.height,
|
||||||
|
pic1.argb_stride * 4,
|
||||||
|
&data);
|
||||||
|
if (data_size == 0) {
|
||||||
|
fprintf(stderr, "Error during lossless encoding.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
|
||||||
|
WebPFree(data);
|
||||||
|
if (ret) goto End;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
End:
|
||||||
|
WebPPictureFree(&pic1);
|
||||||
|
WebPPictureFree(&pic2);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
129
extras/quality_estimate.c
Normal file
129
extras/quality_estimate.c
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// VP8EstimateQuality(): rough encoding quality estimate
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include "./extras.h"
|
||||||
|
#include "webp/decode.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define INVALID_BIT_POS (1ull << 63)
|
||||||
|
|
||||||
|
// In most cases, we don't need to use a full arithmetic decoder, since
|
||||||
|
// all the header's bits are written using a uniform probability of 128.
|
||||||
|
// We can just parse the header as if it was bits (works in 99.999% cases).
|
||||||
|
static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb,
|
||||||
|
uint64_t max_size, uint64_t* const bit_pos) {
|
||||||
|
uint32_t val = 0;
|
||||||
|
if (*bit_pos + nb <= 8 * max_size) {
|
||||||
|
while (nb-- > 0) {
|
||||||
|
const uint64_t p = (*bit_pos)++;
|
||||||
|
const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
|
||||||
|
val = (val << 1) | bit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*bit_pos = INVALID_BIT_POS;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_BIT(n) GetBit(data, (n), size, &bit_pos)
|
||||||
|
#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0)
|
||||||
|
|
||||||
|
int VP8EstimateQuality(const uint8_t* const data, size_t size) {
|
||||||
|
size_t pos = 0;
|
||||||
|
uint64_t bit_pos;
|
||||||
|
uint64_t sig = 0x00;
|
||||||
|
int ok = 0;
|
||||||
|
int Q = -1;
|
||||||
|
WebPBitstreamFeatures features;
|
||||||
|
|
||||||
|
if (data == NULL) return -1;
|
||||||
|
|
||||||
|
if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
|
||||||
|
return -1; // invalid file
|
||||||
|
}
|
||||||
|
if (features.format == 2) return 101; // lossless
|
||||||
|
if (features.format == 0 || features.has_animation) return -1; // mixed
|
||||||
|
|
||||||
|
while (pos < size) {
|
||||||
|
sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
|
||||||
|
if ((sig >> 24) == 0x2a019dull) {
|
||||||
|
ok = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok) return -1;
|
||||||
|
if (pos + 4 > size) return -1;
|
||||||
|
|
||||||
|
// Skip main Header
|
||||||
|
// width = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff;
|
||||||
|
// height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff;
|
||||||
|
pos += 4;
|
||||||
|
bit_pos = pos * 8;
|
||||||
|
|
||||||
|
GET_BIT(2); // color_space + clamp type
|
||||||
|
|
||||||
|
// Segment header
|
||||||
|
if (GET_BIT(1)) { // use_segment_
|
||||||
|
int s;
|
||||||
|
const int update_map = GET_BIT(1);
|
||||||
|
if (GET_BIT(1)) { // update data
|
||||||
|
const int absolute_delta = GET_BIT(1);
|
||||||
|
int q[4] = { 0, 0, 0, 0 };
|
||||||
|
for (s = 0; s < 4; ++s) {
|
||||||
|
if (GET_BIT(1)) {
|
||||||
|
q[s] = GET_BIT(7);
|
||||||
|
if (GET_BIT(1)) q[s] = -q[s]; // sign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (absolute_delta) Q = q[0]; // just use the first segment's quantizer
|
||||||
|
for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
|
||||||
|
}
|
||||||
|
if (update_map) {
|
||||||
|
for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter header
|
||||||
|
GET_BIT(1 + 6 + 3); // simple + level + sharpness
|
||||||
|
if (GET_BIT(1)) { // use_lf_delta
|
||||||
|
if (GET_BIT(1)) { // update lf_delta?
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// num partitions
|
||||||
|
GET_BIT(2);
|
||||||
|
|
||||||
|
// ParseQuant
|
||||||
|
{
|
||||||
|
const int base_q = GET_BIT(7);
|
||||||
|
/* dqy1_dc = */ CONDITIONAL_SKIP(5);
|
||||||
|
/* dqy2_dc = */ CONDITIONAL_SKIP(5);
|
||||||
|
/* dqy2_ac = */ CONDITIONAL_SKIP(5);
|
||||||
|
/* dquv_dc = */ CONDITIONAL_SKIP(5);
|
||||||
|
/* dquv_ac = */ CONDITIONAL_SKIP(5);
|
||||||
|
|
||||||
|
if (Q < 0) Q = base_q;
|
||||||
|
}
|
||||||
|
if (bit_pos == INVALID_BIT_POS) return -1;
|
||||||
|
|
||||||
|
// base mapping
|
||||||
|
Q = (127 - Q) * 100 / 127;
|
||||||
|
// correction for power-law behavior in low range
|
||||||
|
if (Q < 80) {
|
||||||
|
Q = (int)(pow(Q / 80., 1. / 0.38) * 80);
|
||||||
|
}
|
||||||
|
return Q;
|
||||||
|
}
|
||||||
50
extras/webp_quality.c
Normal file
50
extras/webp_quality.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Simple tool to roughly evaluate the quality encoding of a webp bitstream
|
||||||
|
//
|
||||||
|
// Result is a *rough* estimation of the quality. You should just consider
|
||||||
|
// the bucket it's in (q > 80? > 50? > 20?) and not take it for face value.
|
||||||
|
/*
|
||||||
|
gcc -o webp_quality webp_quality.c -O3 -I../ -L. -L../imageio \
|
||||||
|
-limageio_util -lwebpextras -lwebp -lm -lpthread
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "./extras.h"
|
||||||
|
#include "../imageio/imageio_util.h"
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
int c;
|
||||||
|
int quiet = 0;
|
||||||
|
int ok = 1;
|
||||||
|
for (c = 1; ok && c < argc; ++c) {
|
||||||
|
if (!strcmp(argv[c], "-quiet")) {
|
||||||
|
quiet = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
|
||||||
|
printf("webp_quality [-h][-quiet] webp_files...\n");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const char* const filename = argv[c];
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
|
int q;
|
||||||
|
ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
|
if (!ok) break;
|
||||||
|
q = VP8EstimateQuality(data, data_size);
|
||||||
|
if (!quiet) printf("[%s] ", filename);
|
||||||
|
if (q < 0) {
|
||||||
|
fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n");
|
||||||
|
ok = 0;
|
||||||
|
} else {
|
||||||
|
if (!quiet) {
|
||||||
|
printf("Estimated quality factor: %d\n", q);
|
||||||
|
} else {
|
||||||
|
printf("%d\n", q); // just print the number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free((void*)data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
14
gradle.properties
Normal file
14
gradle.properties
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Versions for gradle
|
||||||
|
BUILD_TOOLS_VERSION=23.0.3
|
||||||
|
COMPILE_SDK_VERSION=23
|
||||||
|
ANDROID_GRADLE_PLUGIN_VERSION=1.5.0
|
||||||
|
GRADLE_DOWNLOAD_TASK_VERSION=2.1.0
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Thu May 12 17:06:25 CEST 2016
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip
|
||||||
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable file
@@ -0,0 +1,164 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
53
imageio/Android.mk
Normal file
53
imageio/Android.mk
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libimageio_util
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
imageio_util.c \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
|
|
||||||
|
LOCAL_MODULE := imageio_util
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libimagedec
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
image_dec.c \
|
||||||
|
jpegdec.c \
|
||||||
|
metadata.c \
|
||||||
|
pngdec.c \
|
||||||
|
tiffdec.c \
|
||||||
|
webpdec.c \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
|
LOCAL_STATIC_LIBRARIES := imageio_util
|
||||||
|
|
||||||
|
LOCAL_MODULE := imagedec
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# libimageenc
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
image_enc.c \
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := $(WEBP_CFLAGS)
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
|
||||||
|
LOCAL_STATIC_LIBRARIES := imageio_util
|
||||||
|
|
||||||
|
LOCAL_MODULE := imageenc
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
22
imageio/Makefile.am
Normal file
22
imageio/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
|
||||||
|
noinst_LTLIBRARIES = libimageio_util.la libimagedec.la libimageenc.la
|
||||||
|
|
||||||
|
noinst_HEADERS =
|
||||||
|
noinst_HEADERS += ../src/webp/decode.h
|
||||||
|
noinst_HEADERS += ../src/webp/types.h
|
||||||
|
|
||||||
|
libimageio_util_la_SOURCES = imageio_util.c imageio_util.h
|
||||||
|
|
||||||
|
libimagedec_la_SOURCES = image_dec.c image_dec.h
|
||||||
|
libimagedec_la_SOURCES += jpegdec.c jpegdec.h
|
||||||
|
libimagedec_la_SOURCES += metadata.c metadata.h
|
||||||
|
libimagedec_la_SOURCES += pngdec.c pngdec.h
|
||||||
|
libimagedec_la_SOURCES += tiffdec.c tiffdec.h
|
||||||
|
libimagedec_la_SOURCES += webpdec.c webpdec.h
|
||||||
|
libimagedec_la_SOURCES += wicdec.c wicdec.h
|
||||||
|
libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
|
||||||
|
libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
|
|
||||||
|
libimageenc_la_SOURCES = image_enc.c image_enc.h
|
||||||
|
libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
|
||||||
|
libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
61
imageio/image_dec.c
Normal file
61
imageio/image_dec.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Generic image-type guessing.
|
||||||
|
|
||||||
|
#include "./image_dec.h"
|
||||||
|
|
||||||
|
static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
|
||||||
|
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||||
|
size_t data_size) {
|
||||||
|
WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT;
|
||||||
|
if (data != NULL && data_size >= 12) {
|
||||||
|
const uint32_t magic1 = GetBE32(data + 0);
|
||||||
|
const uint32_t magic2 = GetBE32(data + 8);
|
||||||
|
if (magic1 == 0x89504E47U) {
|
||||||
|
format = WEBP_PNG_FORMAT;
|
||||||
|
} else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
|
||||||
|
format = WEBP_JPEG_FORMAT;
|
||||||
|
} else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
|
||||||
|
format = WEBP_TIFF_FORMAT;
|
||||||
|
} else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
|
||||||
|
format = WEBP_WEBP_FORMAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FailReader(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic,
|
||||||
|
int keep_alpha, struct Metadata* const metadata) {
|
||||||
|
(void)data;
|
||||||
|
(void)data_size;
|
||||||
|
(void)pic;
|
||||||
|
(void)keep_alpha;
|
||||||
|
(void)metadata;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case WEBP_PNG_FORMAT: return ReadPNG;
|
||||||
|
case WEBP_JPEG_FORMAT: return ReadJPEG;
|
||||||
|
case WEBP_TIFF_FORMAT: return ReadTIFF;
|
||||||
|
case WEBP_WEBP_FORMAT: return ReadWebP;
|
||||||
|
default: return FailReader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||||
|
size_t data_size) {
|
||||||
|
return WebPGetImageReader(WebPGuessImageType(data, data_size));
|
||||||
|
}
|
||||||
65
imageio/image_dec.h
Normal file
65
imageio/image_dec.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images.
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||||
|
#define WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "./metadata.h"
|
||||||
|
#include "./jpegdec.h"
|
||||||
|
#include "./pngdec.h"
|
||||||
|
#include "./tiffdec.h"
|
||||||
|
#include "./webpdec.h"
|
||||||
|
#include "./wicdec.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
WEBP_PNG_FORMAT = 0,
|
||||||
|
WEBP_JPEG_FORMAT,
|
||||||
|
WEBP_TIFF_FORMAT,
|
||||||
|
WEBP_WEBP_FORMAT,
|
||||||
|
WEBP_UNSUPPORTED_FORMAT
|
||||||
|
} WebPInputFileFormat;
|
||||||
|
|
||||||
|
// Try to infer the image format. 'data_size' should be larger than 12.
|
||||||
|
// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
|
||||||
|
WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
|
||||||
|
size_t data_size);
|
||||||
|
|
||||||
|
// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
|
||||||
|
typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic,
|
||||||
|
int keep_alpha, struct Metadata* const metadata);
|
||||||
|
|
||||||
|
// Return the reader associated to a given file format.
|
||||||
|
WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
|
||||||
|
|
||||||
|
// This function is similar to WebPGuessImageType(), but returns a
|
||||||
|
// suitable reader function. The returned reader is never NULL, but
|
||||||
|
// unknown formats will return an always-failing valid reader.
|
||||||
|
WebPImageReader WebPGuessImageReader(const uint8_t* const data,
|
||||||
|
size_t data_size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_IMAGEIO_IMAGE_DEC_H_
|
||||||
591
imageio/image_enc.c
Normal file
591
imageio/image_enc.c
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Save image
|
||||||
|
|
||||||
|
#include "./image_enc.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef WEBP_HAVE_PNG
|
||||||
|
#include <png.h>
|
||||||
|
#include <setjmp.h> // note: this must be included *after* png.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
||||||
|
#endif
|
||||||
|
#define CINTERFACE
|
||||||
|
#define COBJMACROS
|
||||||
|
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||||
|
// code with COBJMACROS.
|
||||||
|
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wincodec.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "./imageio_util.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// PNG
|
||||||
|
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
|
||||||
|
#define IFS(fn) \
|
||||||
|
do { \
|
||||||
|
if (SUCCEEDED(hr)) { \
|
||||||
|
hr = (fn); \
|
||||||
|
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define MAKE_REFGUID(x) (x)
|
||||||
|
#else
|
||||||
|
#define MAKE_REFGUID(x) &(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static HRESULT CreateOutputStream(const char* out_file_name,
|
||||||
|
int write_to_mem, IStream** stream) {
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
if (write_to_mem) {
|
||||||
|
// Output to a memory buffer. This is freed when 'stream' is released.
|
||||||
|
IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
|
||||||
|
} else {
|
||||||
|
IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
|
||||||
|
}
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
fprintf(stderr, "Error opening output file %s (%08lx)\n",
|
||||||
|
out_file_name, hr);
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
|
||||||
|
REFGUID container_guid,
|
||||||
|
uint8_t* rgb, int stride,
|
||||||
|
uint32_t width, uint32_t height, int has_alpha) {
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
IWICImagingFactory* factory = NULL;
|
||||||
|
IWICBitmapFrameEncode* frame = NULL;
|
||||||
|
IWICBitmapEncoder* encoder = NULL;
|
||||||
|
IStream* stream = NULL;
|
||||||
|
WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
|
||||||
|
: GUID_WICPixelFormat24bppBGR;
|
||||||
|
|
||||||
|
if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
|
||||||
|
|
||||||
|
IFS(CoInitialize(NULL));
|
||||||
|
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||||
|
CLSCTX_INPROC_SERVER,
|
||||||
|
MAKE_REFGUID(IID_IWICImagingFactory),
|
||||||
|
(LPVOID*)&factory));
|
||||||
|
if (hr == REGDB_E_CLASSNOTREG) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Couldn't access Windows Imaging Component (are you running "
|
||||||
|
"Windows XP SP3 or newer?). PNG support not available. "
|
||||||
|
"Use -ppm or -pgm for available PPM and PGM formats.\n");
|
||||||
|
}
|
||||||
|
IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
|
||||||
|
IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
|
||||||
|
&encoder));
|
||||||
|
IFS(IWICBitmapEncoder_Initialize(encoder, stream,
|
||||||
|
WICBitmapEncoderNoCache));
|
||||||
|
IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
|
||||||
|
IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
|
||||||
|
IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
|
||||||
|
IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
|
||||||
|
IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
|
||||||
|
height * stride, rgb));
|
||||||
|
IFS(IWICBitmapFrameEncode_Commit(frame));
|
||||||
|
IFS(IWICBitmapEncoder_Commit(encoder));
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && use_stdout) {
|
||||||
|
HGLOBAL image;
|
||||||
|
IFS(GetHGlobalFromStream(stream, &image));
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
DWORD mode;
|
||||||
|
const BOOL update_mode = GetConsoleMode(std_output, &mode);
|
||||||
|
const void* const image_mem = GlobalLock(image);
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
|
||||||
|
// Clear output processing if necessary, then output the image.
|
||||||
|
if (update_mode) SetConsoleMode(std_output, 0);
|
||||||
|
if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
|
||||||
|
&bytes_written, NULL) ||
|
||||||
|
bytes_written != GlobalSize(image)) {
|
||||||
|
hr = E_FAIL;
|
||||||
|
}
|
||||||
|
if (update_mode) SetConsoleMode(std_output, mode);
|
||||||
|
GlobalUnlock(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame != NULL) IUnknown_Release(frame);
|
||||||
|
if (encoder != NULL) IUnknown_Release(encoder);
|
||||||
|
if (factory != NULL) IUnknown_Release(factory);
|
||||||
|
if (stream != NULL) IUnknown_Release(stream);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||||
|
const WebPDecBuffer* const buffer) {
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
uint8_t* const rgb = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||||
|
|
||||||
|
return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
|
||||||
|
MAKE_REFGUID(GUID_ContainerFormatPng),
|
||||||
|
rgb, stride, width, height, has_alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
|
||||||
|
static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) {
|
||||||
|
(void)dummy; // remove variable-unused warning
|
||||||
|
longjmp(png_jmpbuf(png), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
png_bytep row = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||||
|
volatile png_structp png;
|
||||||
|
volatile png_infop info;
|
||||||
|
png_uint_32 y;
|
||||||
|
|
||||||
|
if (out_file == NULL || buffer == NULL) return 0;
|
||||||
|
|
||||||
|
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
|
NULL, PNGErrorFunction, NULL);
|
||||||
|
if (png == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
info = png_create_info_struct(png);
|
||||||
|
if (info == NULL) {
|
||||||
|
png_destroy_write_struct((png_structpp)&png, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (setjmp(png_jmpbuf(png))) {
|
||||||
|
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
png_init_io(png, out_file);
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
||||||
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||||
|
PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
png_write_rows(png, &row, 1);
|
||||||
|
row += stride;
|
||||||
|
}
|
||||||
|
png_write_end(png, info);
|
||||||
|
png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
|
||||||
|
int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
if (fout == NULL || buffer == NULL) return 0;
|
||||||
|
|
||||||
|
fprintf(stderr, "PNG support not compiled. Please install the libpng "
|
||||||
|
"development package before building.\n");
|
||||||
|
fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// PPM / PAM
|
||||||
|
|
||||||
|
static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
|
||||||
|
int alpha) {
|
||||||
|
if (fout == NULL || buffer == NULL) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
const uint8_t* row = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const size_t bytes_per_px = alpha ? 4 : 3;
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
if (row == NULL) return 0;
|
||||||
|
|
||||||
|
if (alpha) {
|
||||||
|
fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
|
||||||
|
"TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
|
||||||
|
} else {
|
||||||
|
fprintf(fout, "P6\n%u %u\n255\n", width, height);
|
||||||
|
}
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
row += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
return WritePPMPAM(fout, buffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
return WritePPMPAM(fout, buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Raw PGM
|
||||||
|
|
||||||
|
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
|
||||||
|
int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const uint32_t bytes_per_px = 2;
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||||
|
|
||||||
|
fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rgba += stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// BMP
|
||||||
|
|
||||||
|
static void PutLE16(uint8_t* const dst, uint32_t value) {
|
||||||
|
dst[0] = (value >> 0) & 0xff;
|
||||||
|
dst[1] = (value >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PutLE32(uint8_t* const dst, uint32_t value) {
|
||||||
|
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
||||||
|
PutLE16(dst + 2, (value >> 16) & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BMP_HEADER_SIZE 54
|
||||||
|
int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
|
||||||
|
uint32_t y;
|
||||||
|
const uint32_t line_size = bytes_per_px * width;
|
||||||
|
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
|
||||||
|
const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
|
||||||
|
uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };
|
||||||
|
|
||||||
|
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||||
|
|
||||||
|
// bitmap file header
|
||||||
|
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
||||||
|
PutLE32(bmp_header + 2, total_size); // size including header
|
||||||
|
PutLE32(bmp_header + 6, 0); // reserved
|
||||||
|
PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
|
||||||
|
// bitmap info header
|
||||||
|
PutLE32(bmp_header + 14, 40); // DIB header size
|
||||||
|
PutLE32(bmp_header + 18, width); // dimensions
|
||||||
|
PutLE32(bmp_header + 22, -(int)height); // vertical flip!
|
||||||
|
PutLE16(bmp_header + 26, 1); // number of planes
|
||||||
|
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
||||||
|
PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
|
||||||
|
PutLE32(bmp_header + 34, 0); // image size (dummy)
|
||||||
|
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
||||||
|
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
||||||
|
PutLE32(bmp_header + 46, 0); // number of palette colors
|
||||||
|
PutLE32(bmp_header + 50, 0); // important color count
|
||||||
|
|
||||||
|
// TODO(skal): color profile
|
||||||
|
|
||||||
|
// write header
|
||||||
|
if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write pixel array
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
if (fwrite(rgba, line_size, 1, fout) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// write padding zeroes
|
||||||
|
if (bmp_stride != line_size) {
|
||||||
|
const uint8_t zeroes[3] = { 0 };
|
||||||
|
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rgba += stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#undef BMP_HEADER_SIZE
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// TIFF
|
||||||
|
|
||||||
|
#define NUM_IFD_ENTRIES 15
|
||||||
|
#define EXTRA_DATA_SIZE 16
|
||||||
|
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
|
||||||
|
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
|
||||||
|
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
|
||||||
|
|
||||||
|
int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||||
|
const int stride = buffer->u.RGBA.stride;
|
||||||
|
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
||||||
|
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
||||||
|
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
||||||
|
: NUM_IFD_ENTRIES - 1;
|
||||||
|
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
||||||
|
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
||||||
|
8, 0, 0, 0, // offset to the unique IFD that follows
|
||||||
|
// IFD (offset = 8). Entries must be written in increasing tag order.
|
||||||
|
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
||||||
|
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
||||||
|
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
||||||
|
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
||||||
|
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
||||||
|
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
||||||
|
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
||||||
|
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
||||||
|
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
||||||
|
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
||||||
|
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
||||||
|
bytes_per_px, 0, 0, 0,
|
||||||
|
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
||||||
|
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
||||||
|
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
||||||
|
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||||
|
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
||||||
|
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||||
|
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
||||||
|
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||||
|
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
|
||||||
|
0, 0, 0, 0, // 190: IFD terminator
|
||||||
|
// EXTRA_DATA_OFFSET:
|
||||||
|
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||||
|
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
||||||
|
};
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
|
||||||
|
|
||||||
|
// Fill placeholders in IFD:
|
||||||
|
PutLE32(tiff_header + 10 + 8, width);
|
||||||
|
PutLE32(tiff_header + 22 + 8, height);
|
||||||
|
PutLE32(tiff_header + 106 + 8, height);
|
||||||
|
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
|
||||||
|
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
|
||||||
|
|
||||||
|
// write header
|
||||||
|
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// write pixel values
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
if (fwrite(rgba, bytes_per_px, width, fout) != width) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rgba += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef TIFF_HEADER_SIZE
|
||||||
|
#undef EXTRA_DATA_OFFSET
|
||||||
|
#undef EXTRA_DATA_SIZE
|
||||||
|
#undef NUM_IFD_ENTRIES
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Raw Alpha
|
||||||
|
|
||||||
|
int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
if (fout == NULL || buffer == NULL) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const uint32_t width = buffer->width;
|
||||||
|
const uint32_t height = buffer->height;
|
||||||
|
const uint8_t* a = buffer->u.YUVA.a;
|
||||||
|
const int a_stride = buffer->u.YUVA.a_stride;
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
if (a == NULL) return 0;
|
||||||
|
|
||||||
|
fprintf(fout, "P5\n%u %u\n255\n", width, height);
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
if (fwrite(a, width, 1, fout) != 1) return 0;
|
||||||
|
a += a_stride;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// PGM with IMC4 layout
|
||||||
|
|
||||||
|
int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
if (fout == NULL || buffer == NULL) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const int width = buffer->width;
|
||||||
|
const int height = buffer->height;
|
||||||
|
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
||||||
|
const uint8_t* src_y = yuv->y;
|
||||||
|
const uint8_t* src_u = yuv->u;
|
||||||
|
const uint8_t* src_v = yuv->v;
|
||||||
|
const uint8_t* src_a = yuv->a;
|
||||||
|
const int uv_width = (width + 1) / 2;
|
||||||
|
const int uv_height = (height + 1) / 2;
|
||||||
|
const int a_height = (src_a != NULL) ? height : 0;
|
||||||
|
int ok = 1;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||||
|
|
||||||
|
fprintf(fout, "P5\n%d %d\n255\n",
|
||||||
|
(width + 1) & ~1, height + uv_height + a_height);
|
||||||
|
for (y = 0; ok && y < height; ++y) {
|
||||||
|
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||||
|
if (width & 1) fputc(0, fout); // padding byte
|
||||||
|
src_y += yuv->y_stride;
|
||||||
|
}
|
||||||
|
for (y = 0; ok && y < uv_height; ++y) {
|
||||||
|
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
|
||||||
|
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
|
||||||
|
src_u += yuv->u_stride;
|
||||||
|
src_v += yuv->v_stride;
|
||||||
|
}
|
||||||
|
for (y = 0; ok && y < a_height; ++y) {
|
||||||
|
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||||
|
if (width & 1) fputc(0, fout); // padding byte
|
||||||
|
src_a += yuv->a_stride;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Raw YUV(A) planes
|
||||||
|
|
||||||
|
int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
|
||||||
|
if (fout == NULL || buffer == NULL) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const int width = buffer->width;
|
||||||
|
const int height = buffer->height;
|
||||||
|
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
|
||||||
|
const uint8_t* src_y = yuv->y;
|
||||||
|
const uint8_t* src_u = yuv->u;
|
||||||
|
const uint8_t* src_v = yuv->v;
|
||||||
|
const uint8_t* src_a = yuv->a;
|
||||||
|
const int uv_width = (width + 1) / 2;
|
||||||
|
const int uv_height = (height + 1) / 2;
|
||||||
|
const int a_height = (src_a != NULL) ? height : 0;
|
||||||
|
int ok = 1;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
|
||||||
|
|
||||||
|
for (y = 0; ok && y < height; ++y) {
|
||||||
|
ok &= (fwrite(src_y, width, 1, fout) == 1);
|
||||||
|
src_y += yuv->y_stride;
|
||||||
|
}
|
||||||
|
for (y = 0; ok && y < uv_height; ++y) {
|
||||||
|
ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
|
||||||
|
src_u += yuv->u_stride;
|
||||||
|
}
|
||||||
|
for (y = 0; ok && y < uv_height; ++y) {
|
||||||
|
ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
|
||||||
|
src_v += yuv->v_stride;
|
||||||
|
}
|
||||||
|
for (y = 0; ok && y < a_height; ++y) {
|
||||||
|
ok &= (fwrite(src_a, width, 1, fout) == 1);
|
||||||
|
src_a += yuv->a_stride;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Generic top-level call
|
||||||
|
|
||||||
|
int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||||
|
WebPOutputFileFormat format, const char* const out_file) {
|
||||||
|
FILE* fout = NULL;
|
||||||
|
int needs_open_file = 1;
|
||||||
|
const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-");
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
if (buffer == NULL || out_file == NULL) return 0;
|
||||||
|
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
needs_open_file = (format != PNG);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (needs_open_file) {
|
||||||
|
fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
|
||||||
|
if (fout == NULL) {
|
||||||
|
fprintf(stderr, "Error opening output file %s\n", out_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format == PNG ||
|
||||||
|
format == RGBA || format == BGRA || format == ARGB ||
|
||||||
|
format == rgbA || format == bgrA || format == Argb) {
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
ok &= WebPWritePNG(out_file, use_stdout, buffer);
|
||||||
|
#else
|
||||||
|
ok &= WebPWritePNG(fout, buffer);
|
||||||
|
#endif
|
||||||
|
} else if (format == PAM) {
|
||||||
|
ok &= WebPWritePAM(fout, buffer);
|
||||||
|
} else if (format == PPM || format == RGB || format == BGR) {
|
||||||
|
ok &= WebPWritePPM(fout, buffer);
|
||||||
|
} else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
|
||||||
|
ok &= WebPWrite16bAsPGM(fout, buffer);
|
||||||
|
} else if (format == BMP) {
|
||||||
|
ok &= WebPWriteBMP(fout, buffer);
|
||||||
|
} else if (format == TIFF) {
|
||||||
|
ok &= WebPWriteTIFF(fout, buffer);
|
||||||
|
} else if (format == RAW_YUV) {
|
||||||
|
ok &= WebPWriteYUV(fout, buffer);
|
||||||
|
} else if (format == PGM || format == YUV || format == YUVA) {
|
||||||
|
ok &= WebPWritePGM(fout, buffer);
|
||||||
|
} else if (format == ALPHA_PLANE_ONLY) {
|
||||||
|
ok &= WebPWriteAlphaPlane(fout, buffer);
|
||||||
|
}
|
||||||
|
if (fout != NULL && fout != stdout) {
|
||||||
|
fclose(fout);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
96
imageio/image_enc.h
Normal file
96
imageio/image_enc.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images.
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||||
|
#define WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
#include "webp/decode.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Output types
|
||||||
|
typedef enum {
|
||||||
|
PNG = 0,
|
||||||
|
PAM,
|
||||||
|
PPM,
|
||||||
|
PGM,
|
||||||
|
BMP,
|
||||||
|
TIFF,
|
||||||
|
RAW_YUV,
|
||||||
|
ALPHA_PLANE_ONLY, // this is for experimenting only
|
||||||
|
// forced colorspace output (for testing, mostly)
|
||||||
|
RGB, RGBA, BGR, BGRA, ARGB,
|
||||||
|
RGBA_4444, RGB_565,
|
||||||
|
rgbA, bgrA, Argb, rgbA_4444,
|
||||||
|
YUV, YUVA
|
||||||
|
} WebPOutputFileFormat;
|
||||||
|
|
||||||
|
// General all-purpose call.
|
||||||
|
// Most formats expect a 'buffer' containing RGBA-like samples, except
|
||||||
|
// RAW_YUV, YUV and YUVA formats.
|
||||||
|
// If 'out_file_name' is "-", data is saved to stdout.
|
||||||
|
// Returns false if an error occurred, true otherwise.
|
||||||
|
int WebPSaveImage(const WebPDecBuffer* const buffer,
|
||||||
|
WebPOutputFileFormat format, const char* const out_file_name);
|
||||||
|
|
||||||
|
// Save to PNG.
|
||||||
|
#ifdef HAVE_WINCODEC_H
|
||||||
|
int WebPWritePNG(const char* out_file_name, int use_stdout,
|
||||||
|
const struct WebPDecBuffer* const buffer);
|
||||||
|
#else
|
||||||
|
int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Save to PPM format (RGB, no alpha)
|
||||||
|
int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save to PAM format (= PPM + alpha)
|
||||||
|
int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes.
|
||||||
|
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save as BMP
|
||||||
|
int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save as TIFF
|
||||||
|
int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save the ALPHA plane (only) as a PGM
|
||||||
|
int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save as YUV samples as PGM format (using IMC4 layout).
|
||||||
|
// See: http://www.fourcc.org/yuv.php#IMC4.
|
||||||
|
// (very convenient format for viewing the samples, esp. for odd dimensions).
|
||||||
|
int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save YUV(A) planes sequentially (raw dump)
|
||||||
|
int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes.
|
||||||
|
int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_IMAGEIO_IMAGE_ENC_H_
|
||||||
143
imageio/imageio_util.c
Normal file
143
imageio/imageio_util.c
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utility functions used by the image decoders.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "./imageio_util.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <fcntl.h> // for _O_BINARY
|
||||||
|
#include <io.h> // for _setmode()
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// File I/O
|
||||||
|
|
||||||
|
FILE* ImgIoUtilSetBinaryMode(FILE* file) {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (_setmode(_fileno(file), _O_BINARY) == -1) {
|
||||||
|
fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
|
||||||
|
static const size_t kBlockSize = 16384; // default initial size
|
||||||
|
size_t max_size = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
uint8_t* input = NULL;
|
||||||
|
|
||||||
|
if (data == NULL || data_size == NULL) return 0;
|
||||||
|
*data = NULL;
|
||||||
|
*data_size = 0;
|
||||||
|
|
||||||
|
if (!ImgIoUtilSetBinaryMode(stdin)) return 0;
|
||||||
|
|
||||||
|
while (!feof(stdin)) {
|
||||||
|
// We double the buffer size each time and read as much as possible.
|
||||||
|
const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
|
||||||
|
void* const new_data = realloc(input, max_size + extra_size);
|
||||||
|
if (new_data == NULL) goto Error;
|
||||||
|
input = (uint8_t*)new_data;
|
||||||
|
max_size += extra_size;
|
||||||
|
size += fread(input + size, 1, extra_size, stdin);
|
||||||
|
if (size < max_size) break;
|
||||||
|
}
|
||||||
|
if (ferror(stdin)) goto Error;
|
||||||
|
*data = input;
|
||||||
|
*data_size = size;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
Error:
|
||||||
|
free(input);
|
||||||
|
fprintf(stderr, "Could not read from stdin\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImgIoUtilReadFile(const char* const file_name,
|
||||||
|
const uint8_t** data, size_t* data_size) {
|
||||||
|
int ok;
|
||||||
|
void* file_data;
|
||||||
|
size_t file_size;
|
||||||
|
FILE* in;
|
||||||
|
const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
|
||||||
|
|
||||||
|
if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
|
||||||
|
|
||||||
|
if (data == NULL || data_size == NULL) return 0;
|
||||||
|
*data = NULL;
|
||||||
|
*data_size = 0;
|
||||||
|
|
||||||
|
in = fopen(file_name, "rb");
|
||||||
|
if (in == NULL) {
|
||||||
|
fprintf(stderr, "cannot open input file '%s'\n", file_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
file_size = ftell(in);
|
||||||
|
fseek(in, 0, SEEK_SET);
|
||||||
|
file_data = malloc(file_size);
|
||||||
|
if (file_data == NULL) return 0;
|
||||||
|
ok = (fread(file_data, file_size, 1, in) == 1);
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Could not read %d bytes of data from file %s\n",
|
||||||
|
(int)file_size, file_name);
|
||||||
|
free(file_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*data = (uint8_t*)file_data;
|
||||||
|
*data_size = file_size;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImgIoUtilWriteFile(const char* const file_name,
|
||||||
|
const uint8_t* data, size_t data_size) {
|
||||||
|
int ok;
|
||||||
|
FILE* out;
|
||||||
|
const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
out = to_stdout ? stdout : fopen(file_name, "wb");
|
||||||
|
if (out == NULL) {
|
||||||
|
fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ok = (fwrite(data, data_size, 1, out) == 1);
|
||||||
|
if (out != stdout) fclose(out);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||||
|
uint8_t* dst, int dst_stride, int width, int height) {
|
||||||
|
while (height-- > 0) {
|
||||||
|
memcpy(dst, src, width * sizeof(*dst));
|
||||||
|
src += src_stride;
|
||||||
|
dst += dst_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
|
||||||
|
const uint64_t total_size = nmemb * size;
|
||||||
|
return (total_size == (size_t)total_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
61
imageio/imageio_util.h
Normal file
61
imageio/imageio_util.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2016 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utility functions used by the image decoders.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||||
|
#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File I/O
|
||||||
|
|
||||||
|
// Reopen file in binary (O_BINARY) mode.
|
||||||
|
// Returns 'file' on success, NULL otherwise.
|
||||||
|
FILE* ImgIoUtilSetBinaryMode(FILE* file);
|
||||||
|
|
||||||
|
// Allocates storage for entire file 'file_name' and returns contents and size
|
||||||
|
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
||||||
|
// be deleted using free().
|
||||||
|
// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
|
||||||
|
// the function ImgIoUtilReadFromStdin().
|
||||||
|
int ImgIoUtilReadFile(const char* const file_name,
|
||||||
|
const uint8_t** data, size_t* data_size);
|
||||||
|
|
||||||
|
// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
|
||||||
|
int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
|
||||||
|
|
||||||
|
// Write a data segment into a file named 'file_name'. Returns true if ok.
|
||||||
|
// If 'file_name' is NULL or equal to "-", output is written to stdout.
|
||||||
|
int ImgIoUtilWriteFile(const char* const file_name,
|
||||||
|
const uint8_t* data, size_t data_size);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
||||||
|
void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
|
||||||
|
uint8_t* dst, int dst_stride, int width, int height);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Returns 0 in case of overflow of nmemb * size.
|
||||||
|
int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
|
||||||
@@ -19,11 +19,13 @@
|
|||||||
|
|
||||||
#ifdef WEBP_HAVE_JPEG
|
#ifdef WEBP_HAVE_JPEG
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include <jerror.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "webp/encode.h"
|
#include "webp/encode.h"
|
||||||
|
#include "./imageio_util.h"
|
||||||
#include "./metadata.h"
|
#include "./metadata.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -208,13 +210,68 @@ static void my_error_exit(j_common_ptr dinfo) {
|
|||||||
longjmp(myerr->setjmp_buffer, 1);
|
longjmp(myerr->setjmp_buffer, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
|
typedef struct {
|
||||||
int ok = 0;
|
struct jpeg_source_mgr pub;
|
||||||
int stride, width, height;
|
const uint8_t* data;
|
||||||
|
size_t data_size;
|
||||||
|
} JPEGReadContext;
|
||||||
|
|
||||||
|
static void ContextInit(j_decompress_ptr cinfo) {
|
||||||
|
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
|
||||||
|
ctx->pub.next_input_byte = ctx->data;
|
||||||
|
ctx->pub.bytes_in_buffer = ctx->data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean ContextFill(j_decompress_ptr cinfo) {
|
||||||
|
// we shouldn't get here.
|
||||||
|
ERREXIT(cinfo, JERR_FILE_READ);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
|
||||||
|
JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
|
||||||
|
size_t jump = (size_t)jump_size;
|
||||||
|
if (jump > ctx->pub.bytes_in_buffer) { // Don't overflow the buffer.
|
||||||
|
jump = ctx->pub.bytes_in_buffer;
|
||||||
|
}
|
||||||
|
ctx->pub.bytes_in_buffer -= jump;
|
||||||
|
ctx->pub.next_input_byte += jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ContextTerm(j_decompress_ptr cinfo) {
|
||||||
|
(void)cinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
|
||||||
|
JPEGReadContext* const ctx) {
|
||||||
|
cinfo->src = (struct jpeg_source_mgr*)ctx;
|
||||||
|
ctx->pub.init_source = ContextInit;
|
||||||
|
ctx->pub.fill_input_buffer = ContextFill;
|
||||||
|
ctx->pub.skip_input_data = ContextSkip;
|
||||||
|
ctx->pub.resync_to_restart = jpeg_resync_to_restart;
|
||||||
|
ctx->pub.term_source = ContextTerm;
|
||||||
|
ctx->pub.bytes_in_buffer = 0;
|
||||||
|
ctx->pub.next_input_byte = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||||
|
WebPPicture* const pic, int keep_alpha,
|
||||||
|
Metadata* const metadata) {
|
||||||
|
volatile int ok = 0;
|
||||||
|
int width, height;
|
||||||
|
int64_t stride;
|
||||||
volatile struct jpeg_decompress_struct dinfo;
|
volatile struct jpeg_decompress_struct dinfo;
|
||||||
struct my_error_mgr jerr;
|
struct my_error_mgr jerr;
|
||||||
uint8_t* volatile rgb = NULL;
|
uint8_t* volatile rgb = NULL;
|
||||||
JSAMPROW buffer[1];
|
JSAMPROW buffer[1];
|
||||||
|
JPEGReadContext ctx;
|
||||||
|
|
||||||
|
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||||
|
|
||||||
|
(void)keep_alpha;
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
ctx.data = data;
|
||||||
|
ctx.data_size = data_size;
|
||||||
|
|
||||||
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp sanity
|
memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp sanity
|
||||||
dinfo.err = jpeg_std_error(&jerr.pub);
|
dinfo.err = jpeg_std_error(&jerr.pub);
|
||||||
@@ -228,7 +285,7 @@ int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jpeg_create_decompress((j_decompress_ptr)&dinfo);
|
jpeg_create_decompress((j_decompress_ptr)&dinfo);
|
||||||
jpeg_stdio_src((j_decompress_ptr)&dinfo, in_file);
|
ContextSetup(&dinfo, &ctx);
|
||||||
if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
|
if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
|
||||||
jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
|
jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
|
||||||
|
|
||||||
@@ -243,9 +300,14 @@ int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
|
|||||||
|
|
||||||
width = dinfo.output_width;
|
width = dinfo.output_width;
|
||||||
height = dinfo.output_height;
|
height = dinfo.output_height;
|
||||||
stride = dinfo.output_width * dinfo.output_components * sizeof(*rgb);
|
stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
|
||||||
|
|
||||||
rgb = (uint8_t*)malloc(stride * height);
|
if (stride != (int)stride ||
|
||||||
|
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||||
if (rgb == NULL) {
|
if (rgb == NULL) {
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
@@ -272,8 +334,7 @@ int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
|
|||||||
// WebP conversion.
|
// WebP conversion.
|
||||||
pic->width = width;
|
pic->width = width;
|
||||||
pic->height = height;
|
pic->height = height;
|
||||||
pic->use_argb = 1; // store raw RGB samples
|
ok = WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||||
ok = WebPPictureImportRGB(pic, rgb, stride);
|
|
||||||
if (!ok) goto Error;
|
if (!ok) goto Error;
|
||||||
|
|
||||||
End:
|
End:
|
||||||
@@ -281,10 +342,13 @@ int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
#else // !WEBP_HAVE_JPEG
|
#else // !WEBP_HAVE_JPEG
|
||||||
int ReadJPEG(FILE* in_file, struct WebPPicture* const pic,
|
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic, int keep_alpha,
|
||||||
struct Metadata* const metadata) {
|
struct Metadata* const metadata) {
|
||||||
(void)in_file;
|
(void)data;
|
||||||
|
(void)data_size;
|
||||||
(void)pic;
|
(void)pic;
|
||||||
|
(void)keep_alpha;
|
||||||
(void)metadata;
|
(void)metadata;
|
||||||
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
|
fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
|
||||||
"development package before building.\n");
|
"development package before building.\n");
|
||||||
@@ -9,10 +9,9 @@
|
|||||||
//
|
//
|
||||||
// JPEG decode.
|
// JPEG decode.
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_JPEGDEC_H_
|
#ifndef WEBP_IMAGEIO_JPEGDEC_H_
|
||||||
#define WEBP_EXAMPLES_JPEGDEC_H_
|
#define WEBP_IMAGEIO_JPEGDEC_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "webp/types.h"
|
#include "webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -22,14 +21,17 @@ extern "C" {
|
|||||||
struct Metadata;
|
struct Metadata;
|
||||||
struct WebPPicture;
|
struct WebPPicture;
|
||||||
|
|
||||||
// Reads a JPEG from 'in_file', returning the decoded output in 'pic'.
|
// Reads a JPEG from 'data', returning the decoded output in 'pic'.
|
||||||
// The output is RGB.
|
// The output is RGB or YUV depending on pic->use_argb value.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
int ReadJPEG(FILE* in_file, struct WebPPicture* const pic,
|
// 'keep_alpha' has no effect, but is kept for coherence with other signatures
|
||||||
|
// for image readers.
|
||||||
|
int ReadJPEG(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic, int keep_alpha,
|
||||||
struct Metadata* const metadata);
|
struct Metadata* const metadata);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_JPEGDEC_H_
|
#endif // WEBP_IMAGEIO_JPEGDEC_H_
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
// Metadata types and functions.
|
// Metadata types and functions.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_METADATA_H_
|
#ifndef WEBP_IMAGEIO_METADATA_H_
|
||||||
#define WEBP_EXAMPLES_METADATA_H_
|
#define WEBP_IMAGEIO_METADATA_H_
|
||||||
|
|
||||||
#include "webp/types.h"
|
#include "webp/types.h"
|
||||||
|
|
||||||
@@ -44,4 +44,4 @@ int MetadataCopy(const char* metadata, size_t metadata_len,
|
|||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_METADATA_H_
|
#endif // WEBP_IMAGEIO_METADATA_H_
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "webp/encode.h"
|
#include "webp/encode.h"
|
||||||
|
#include "./imageio_util.h"
|
||||||
#include "./metadata.h"
|
#include "./metadata.h"
|
||||||
|
|
||||||
static void PNGAPI error_function(png_structp png, png_const_charp error) {
|
static void PNGAPI error_function(png_structp png, png_const_charp error) {
|
||||||
@@ -131,8 +132,8 @@ static int ExtractMetadataFromPNG(png_structp png,
|
|||||||
for (p = 0; p < 2; ++p) {
|
for (p = 0; p < 2; ++p) {
|
||||||
png_infop const info = (p == 0) ? head_info : end_info;
|
png_infop const info = (p == 0) ? head_info : end_info;
|
||||||
png_textp text = NULL;
|
png_textp text = NULL;
|
||||||
const int num = png_get_text(png, info, &text, NULL);
|
const png_uint_32 num = png_get_text(png, info, &text, NULL);
|
||||||
int i;
|
png_uint_32 i;
|
||||||
// Look for EXIF / XMP metadata.
|
// Look for EXIF / XMP metadata.
|
||||||
for (i = 0; i < num; ++i, ++text) {
|
for (i = 0; i < num; ++i, ++text) {
|
||||||
int j;
|
int j;
|
||||||
@@ -188,24 +189,44 @@ static int ExtractMetadataFromPNG(png_structp png,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
|
typedef struct {
|
||||||
Metadata* const metadata) {
|
const uint8_t* data;
|
||||||
volatile png_structp png;
|
size_t data_size;
|
||||||
|
png_size_t offset;
|
||||||
|
} PNGReadContext;
|
||||||
|
|
||||||
|
static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||||
|
PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr);
|
||||||
|
if (ctx->data_size - ctx->offset < length) {
|
||||||
|
png_error(png_ptr, "ReadFunc: invalid read length (overflow)!");
|
||||||
|
}
|
||||||
|
memcpy(data, ctx->data + ctx->offset, length);
|
||||||
|
ctx->offset += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic,
|
||||||
|
int keep_alpha, struct Metadata* const metadata) {
|
||||||
|
volatile png_structp png = NULL;
|
||||||
volatile png_infop info = NULL;
|
volatile png_infop info = NULL;
|
||||||
volatile png_infop end_info = NULL;
|
volatile png_infop end_info = NULL;
|
||||||
|
PNGReadContext context = { NULL, 0, 0 };
|
||||||
int color_type, bit_depth, interlaced;
|
int color_type, bit_depth, interlaced;
|
||||||
int has_alpha;
|
int has_alpha;
|
||||||
int num_passes;
|
int num_passes;
|
||||||
int p;
|
int p;
|
||||||
int ok = 0;
|
volatile int ok = 0;
|
||||||
png_uint_32 width, height, y;
|
png_uint_32 width, height, y;
|
||||||
int stride;
|
int64_t stride;
|
||||||
uint8_t* volatile rgb = NULL;
|
uint8_t* volatile rgb = NULL;
|
||||||
|
|
||||||
|
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||||
|
|
||||||
|
context.data = data;
|
||||||
|
context.data_size = data_size;
|
||||||
|
|
||||||
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||||
if (png == NULL) {
|
if (png == NULL) goto End;
|
||||||
goto End;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_set_error_fn(png, 0, error_function, NULL);
|
png_set_error_fn(png, 0, error_function, NULL);
|
||||||
if (setjmp(png_jmpbuf(png))) {
|
if (setjmp(png_jmpbuf(png))) {
|
||||||
@@ -219,7 +240,7 @@ int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
|
|||||||
end_info = png_create_info_struct(png);
|
end_info = png_create_info_struct(png);
|
||||||
if (end_info == NULL) goto Error;
|
if (end_info == NULL) goto Error;
|
||||||
|
|
||||||
png_init_io(png, in_file);
|
png_set_read_fn(png, &context, ReadFunc);
|
||||||
png_read_info(png, info);
|
png_read_info(png, info);
|
||||||
if (!png_get_IHDR(png, info,
|
if (!png_get_IHDR(png, info,
|
||||||
&width, &height, &bit_depth, &color_type, &interlaced,
|
&width, &height, &bit_depth, &color_type, &interlaced,
|
||||||
@@ -251,13 +272,20 @@ int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
|
|||||||
|
|
||||||
num_passes = png_set_interlace_handling(png);
|
num_passes = png_set_interlace_handling(png);
|
||||||
png_read_update_info(png, info);
|
png_read_update_info(png, info);
|
||||||
stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
|
||||||
rgb = (uint8_t*)malloc(stride * height);
|
stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
||||||
|
if (stride != (int)stride ||
|
||||||
|
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb = (uint8_t*)malloc((size_t)stride * height);
|
||||||
if (rgb == NULL) goto Error;
|
if (rgb == NULL) goto Error;
|
||||||
for (p = 0; p < num_passes; ++p) {
|
for (p = 0; p < num_passes; ++p) {
|
||||||
|
png_bytep row = rgb;
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
png_bytep row = (png_bytep)(rgb + y * stride);
|
|
||||||
png_read_rows(png, &row, NULL, 1);
|
png_read_rows(png, &row, NULL, 1);
|
||||||
|
row += stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
png_read_end(png, end_info);
|
png_read_end(png, end_info);
|
||||||
@@ -268,11 +296,10 @@ int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
|
|||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
pic->width = width;
|
pic->width = (int)width;
|
||||||
pic->height = height;
|
pic->height = (int)height;
|
||||||
pic->use_argb = 1;
|
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
|
||||||
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
|
: WebPPictureImportRGB(pic, rgb, (int)stride);
|
||||||
: WebPPictureImportRGB(pic, rgb, stride);
|
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto Error;
|
goto Error;
|
||||||
@@ -287,9 +314,11 @@ int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
#else // !WEBP_HAVE_PNG
|
#else // !WEBP_HAVE_PNG
|
||||||
int ReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
|
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||||
struct Metadata* const metadata) {
|
struct WebPPicture* const pic,
|
||||||
(void)in_file;
|
int keep_alpha, struct Metadata* const metadata) {
|
||||||
|
(void)data;
|
||||||
|
(void)data_size;
|
||||||
(void)pic;
|
(void)pic;
|
||||||
(void)keep_alpha;
|
(void)keep_alpha;
|
||||||
(void)metadata;
|
(void)metadata;
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
//
|
//
|
||||||
// PNG decode.
|
// PNG decode.
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_PNGDEC_H_
|
#ifndef WEBP_IMAGEIO_PNGDEC_H_
|
||||||
#define WEBP_EXAMPLES_PNGDEC_H_
|
#define WEBP_IMAGEIO_PNGDEC_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -21,15 +21,17 @@ extern "C" {
|
|||||||
struct Metadata;
|
struct Metadata;
|
||||||
struct WebPPicture;
|
struct WebPPicture;
|
||||||
|
|
||||||
// Reads a PNG from 'in_file', returning the decoded output in 'pic'.
|
// Reads a PNG from 'data', returning the decoded output in 'pic'.
|
||||||
|
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||||
// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
|
// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
|
||||||
// otherwise it will be RGB.
|
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
int ReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
|
int ReadPNG(const uint8_t* const data, size_t data_size,
|
||||||
struct Metadata* const metadata);
|
struct WebPPicture* const pic,
|
||||||
|
int keep_alpha, struct Metadata* const metadata);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_PNGDEC_H_
|
#endif // WEBP_IMAGEIO_PNGDEC_H_
|
||||||
@@ -16,11 +16,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef WEBP_HAVE_TIFF
|
#ifdef WEBP_HAVE_TIFF
|
||||||
#include <tiffio.h>
|
#include <tiffio.h>
|
||||||
|
|
||||||
#include "webp/encode.h"
|
#include "webp/encode.h"
|
||||||
|
#include "./imageio_util.h"
|
||||||
#include "./metadata.h"
|
#include "./metadata.h"
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
@@ -63,17 +65,75 @@ static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadTIFF(const char* const filename,
|
// Ad-hoc structure to supply read-from-memory functionalities.
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t* data;
|
||||||
|
toff_t size;
|
||||||
|
toff_t pos;
|
||||||
|
} MyData;
|
||||||
|
|
||||||
|
static int MyClose(thandle_t opaque) {
|
||||||
|
(void)opaque;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static toff_t MySize(thandle_t opaque) {
|
||||||
|
const MyData* const my_data = (MyData*)opaque;
|
||||||
|
return my_data->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
|
||||||
|
MyData* const my_data = (MyData*)opaque;
|
||||||
|
offset += (whence == SEEK_CUR) ? my_data->pos
|
||||||
|
: (whence == SEEK_SET) ? 0
|
||||||
|
: my_data->size;
|
||||||
|
if (offset > my_data->size) return (toff_t)-1;
|
||||||
|
my_data->pos = offset;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
|
||||||
|
(void)opaque;
|
||||||
|
(void)base;
|
||||||
|
(void)size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
|
||||||
|
(void)opaque;
|
||||||
|
(void)base;
|
||||||
|
(void)size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
|
||||||
|
MyData* const my_data = (MyData*)opaque;
|
||||||
|
if (my_data->pos + size > my_data->size) {
|
||||||
|
size = my_data->size - my_data->pos;
|
||||||
|
}
|
||||||
|
if (size > 0) {
|
||||||
|
memcpy(dst, my_data->data + my_data->pos, size);
|
||||||
|
my_data->pos += size;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||||
WebPPicture* const pic, int keep_alpha,
|
WebPPicture* const pic, int keep_alpha,
|
||||||
Metadata* const metadata) {
|
Metadata* const metadata) {
|
||||||
TIFF* const tif = TIFFOpen(filename, "r");
|
MyData my_data = { data, (toff_t)data_size, 0 };
|
||||||
|
TIFF* tif;
|
||||||
uint32 width, height;
|
uint32 width, height;
|
||||||
uint32* raster;
|
uint32* raster;
|
||||||
|
int64_t alloc_size;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
tdir_t dircount;
|
tdir_t dircount;
|
||||||
|
|
||||||
|
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||||
|
|
||||||
|
tif = TIFFClientOpen("Memory", "r", &my_data,
|
||||||
|
MyRead, MyRead, MySeek, MyClose,
|
||||||
|
MySize, MyMapFile, MyUnmapFile);
|
||||||
if (tif == NULL) {
|
if (tif == NULL) {
|
||||||
fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
|
fprintf(stderr, "Error! Cannot parse TIFF file\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +147,18 @@ int ReadTIFF(const char* const filename,
|
|||||||
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
|
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
|
||||||
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
|
||||||
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
||||||
return 0;
|
goto End;
|
||||||
}
|
}
|
||||||
raster = (uint32*)_TIFFmalloc(width * height * sizeof(*raster));
|
|
||||||
|
if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
|
||||||
|
sizeof(*raster))) {
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
// _Tiffmalloc uses a signed type for size.
|
||||||
|
alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
|
||||||
|
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
||||||
|
|
||||||
|
raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
|
||||||
if (raster != NULL) {
|
if (raster != NULL) {
|
||||||
if (TIFFReadRGBAImageOriented(tif, width, height, raster,
|
if (TIFFReadRGBAImageOriented(tif, width, height, raster,
|
||||||
ORIENTATION_TOPLEFT, 1)) {
|
ORIENTATION_TOPLEFT, 1)) {
|
||||||
@@ -100,7 +169,6 @@ int ReadTIFF(const char* const filename,
|
|||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
TIFFSwabArrayOfLong(raster, width * height);
|
TIFFSwabArrayOfLong(raster, width * height);
|
||||||
#endif
|
#endif
|
||||||
pic->use_argb = 1;
|
|
||||||
ok = keep_alpha
|
ok = keep_alpha
|
||||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
|
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
|
||||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
|
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
|
||||||
@@ -120,15 +188,16 @@ int ReadTIFF(const char* const filename,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
End:
|
||||||
TIFFClose(tif);
|
TIFFClose(tif);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
#else // !WEBP_HAVE_TIFF
|
#else // !WEBP_HAVE_TIFF
|
||||||
int ReadTIFF(const char* const filename,
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||||
struct WebPPicture* const pic, int keep_alpha,
|
struct WebPPicture* const pic, int keep_alpha,
|
||||||
struct Metadata* const metadata) {
|
struct Metadata* const metadata) {
|
||||||
(void)filename;
|
(void)data;
|
||||||
|
(void)data_size;
|
||||||
(void)pic;
|
(void)pic;
|
||||||
(void)keep_alpha;
|
(void)keep_alpha;
|
||||||
(void)metadata;
|
(void)metadata;
|
||||||
@@ -9,8 +9,10 @@
|
|||||||
//
|
//
|
||||||
// TIFF decode.
|
// TIFF decode.
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_TIFFDEC_H_
|
#ifndef WEBP_IMAGEIO_TIFFDEC_H_
|
||||||
#define WEBP_EXAMPLES_TIFFDEC_H_
|
#define WEBP_IMAGEIO_TIFFDEC_H_
|
||||||
|
|
||||||
|
#include "webp/types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -19,11 +21,12 @@ extern "C" {
|
|||||||
struct Metadata;
|
struct Metadata;
|
||||||
struct WebPPicture;
|
struct WebPPicture;
|
||||||
|
|
||||||
// Reads a TIFF from 'filename', returning the decoded output in 'pic'.
|
// Reads a TIFF from 'data', returning the decoded output in 'pic'.
|
||||||
|
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||||
// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
|
// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
|
||||||
// otherwise it will be RGB.
|
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
int ReadTIFF(const char* const filename,
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||||
struct WebPPicture* const pic, int keep_alpha,
|
struct WebPPicture* const pic, int keep_alpha,
|
||||||
struct Metadata* const metadata);
|
struct Metadata* const metadata);
|
||||||
|
|
||||||
@@ -31,4 +34,4 @@ int ReadTIFF(const char* const filename,
|
|||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_TIFFDEC_H_
|
#endif // WEBP_IMAGEIO_TIFFDEC_H_
|
||||||
190
imageio/webpdec.c
Normal file
190
imageio/webpdec.c
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2014 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 decode.
|
||||||
|
|
||||||
|
#include "./webpdec.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "webp/decode.h"
|
||||||
|
#include "webp/encode.h"
|
||||||
|
#include "./imageio_util.h"
|
||||||
|
#include "./metadata.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// WebP decoding
|
||||||
|
|
||||||
|
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
|
||||||
|
"OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
|
||||||
|
"UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
|
||||||
|
if (config->input.has_animation) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error! Decoding of an animated WebP file is not supported.\n"
|
||||||
|
" Use webpmux to extract the individual frames or\n"
|
||||||
|
" vwebp to view this image.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintWebPError(const char* const in_file, int status) {
|
||||||
|
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
||||||
|
fprintf(stderr, "Status: %d", status);
|
||||||
|
if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||||
|
fprintf(stderr, "(%s)", kStatusMessages[status]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int LoadWebP(const char* const in_file,
|
||||||
|
const uint8_t** data, size_t* data_size,
|
||||||
|
WebPBitstreamFeatures* bitstream) {
|
||||||
|
VP8StatusCode status;
|
||||||
|
WebPBitstreamFeatures local_features;
|
||||||
|
if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
|
||||||
|
|
||||||
|
if (bitstream == NULL) {
|
||||||
|
bitstream = &local_features;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = WebPGetFeatures(*data, *data_size, bitstream);
|
||||||
|
if (status != VP8_STATUS_OK) {
|
||||||
|
free((void*)*data);
|
||||||
|
*data = NULL;
|
||||||
|
*data_size = 0;
|
||||||
|
PrintWebPError(in_file, status);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||||
|
WebPDecoderConfig* const config) {
|
||||||
|
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||||
|
PrintAnimationWarning(config);
|
||||||
|
return WebPDecode(data, data_size, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
VP8StatusCode DecodeWebPIncremental(
|
||||||
|
const uint8_t* const data, size_t data_size,
|
||||||
|
WebPDecoderConfig* const config) {
|
||||||
|
VP8StatusCode status = VP8_STATUS_OK;
|
||||||
|
if (config == NULL) return VP8_STATUS_INVALID_PARAM;
|
||||||
|
|
||||||
|
PrintAnimationWarning(config);
|
||||||
|
|
||||||
|
// Decoding call.
|
||||||
|
{
|
||||||
|
WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
|
||||||
|
if (idec == NULL) {
|
||||||
|
fprintf(stderr, "Failed during WebPINewDecoder().\n");
|
||||||
|
return VP8_STATUS_OUT_OF_MEMORY;
|
||||||
|
} else {
|
||||||
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||||
|
size_t size = 0;
|
||||||
|
const size_t incr = 2 + (data_size / 20);
|
||||||
|
while (size < data_size) {
|
||||||
|
size_t next_size = size + (rand() % incr);
|
||||||
|
if (next_size > data_size) next_size = data_size;
|
||||||
|
status = WebPIUpdate(idec, data, next_size);
|
||||||
|
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) break;
|
||||||
|
size = next_size;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
status = WebPIUpdate(idec, data, data_size);
|
||||||
|
#endif
|
||||||
|
WebPIDelete(idec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||||
|
WebPPicture* const pic,
|
||||||
|
int keep_alpha, Metadata* const metadata) {
|
||||||
|
int ok = 0;
|
||||||
|
VP8StatusCode status = VP8_STATUS_OK;
|
||||||
|
WebPDecoderConfig config;
|
||||||
|
WebPDecBuffer* const output_buffer = &config.output;
|
||||||
|
WebPBitstreamFeatures* const bitstream = &config.input;
|
||||||
|
|
||||||
|
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
||||||
|
|
||||||
|
// TODO(jzern): add Exif/XMP/ICC extraction.
|
||||||
|
if (metadata != NULL) {
|
||||||
|
fprintf(stderr, "Warning: metadata extraction from WebP is unsupported.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WebPInitDecoderConfig(&config)) {
|
||||||
|
fprintf(stderr, "Library version mismatch!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = WebPGetFeatures(data, data_size, bitstream);
|
||||||
|
if (status != VP8_STATUS_OK) {
|
||||||
|
PrintWebPError("input data", status);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const int has_alpha = keep_alpha && bitstream->has_alpha;
|
||||||
|
if (pic->use_argb) {
|
||||||
|
output_buffer->colorspace = has_alpha ? MODE_RGBA : MODE_RGB;
|
||||||
|
} else {
|
||||||
|
output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = DecodeWebP(data, data_size, &config);
|
||||||
|
if (status == VP8_STATUS_OK) {
|
||||||
|
pic->width = output_buffer->width;
|
||||||
|
pic->height = output_buffer->height;
|
||||||
|
if (pic->use_argb) {
|
||||||
|
const uint8_t* const rgba = output_buffer->u.RGBA.rgba;
|
||||||
|
const int stride = output_buffer->u.RGBA.stride;
|
||||||
|
ok = has_alpha ? WebPPictureImportRGBA(pic, rgba, stride)
|
||||||
|
: WebPPictureImportRGB(pic, rgba, stride);
|
||||||
|
} else {
|
||||||
|
pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
|
||||||
|
ok = WebPPictureAlloc(pic);
|
||||||
|
if (!ok) {
|
||||||
|
status = VP8_STATUS_OUT_OF_MEMORY;
|
||||||
|
} else {
|
||||||
|
const WebPYUVABuffer* const yuva = &output_buffer->u.YUVA;
|
||||||
|
const int uv_width = (pic->width + 1) >> 1;
|
||||||
|
const int uv_height = (pic->height + 1) >> 1;
|
||||||
|
ImgIoUtilCopyPlane(yuva->y, yuva->y_stride,
|
||||||
|
pic->y, pic->y_stride, pic->width, pic->height);
|
||||||
|
ImgIoUtilCopyPlane(yuva->u, yuva->u_stride,
|
||||||
|
pic->u, pic->uv_stride, uv_width, uv_height);
|
||||||
|
ImgIoUtilCopyPlane(yuva->v, yuva->v_stride,
|
||||||
|
pic->v, pic->uv_stride, uv_width, uv_height);
|
||||||
|
if (has_alpha) {
|
||||||
|
ImgIoUtilCopyPlane(yuva->a, yuva->a_stride,
|
||||||
|
pic->a, pic->a_stride, pic->width, pic->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != VP8_STATUS_OK) {
|
||||||
|
PrintWebPError("input data", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPFreeDecBuffer(output_buffer);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
67
imageio/webpdec.h
Normal file
67
imageio/webpdec.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2014 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 decode.
|
||||||
|
|
||||||
|
#ifndef WEBP_IMAGEIO_WEBPDEC_H_
|
||||||
|
#define WEBP_IMAGEIO_WEBPDEC_H_
|
||||||
|
|
||||||
|
#include "webp/decode.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Metadata;
|
||||||
|
struct WebPPicture;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// WebP decoding
|
||||||
|
|
||||||
|
// Prints an informative error message regarding decode failure of 'in_file'.
|
||||||
|
// 'status' is treated as a VP8StatusCode and if valid will be printed as a
|
||||||
|
// text string.
|
||||||
|
void PrintWebPError(const char* const in_file, int status);
|
||||||
|
|
||||||
|
// Reads a WebP from 'in_file', returning the contents and size in 'data' and
|
||||||
|
// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
|
||||||
|
// Returns true on success.
|
||||||
|
int LoadWebP(const char* const in_file,
|
||||||
|
const uint8_t** data, size_t* data_size,
|
||||||
|
WebPBitstreamFeatures* bitstream);
|
||||||
|
|
||||||
|
// Decodes the WebP contained in 'data'.
|
||||||
|
// 'config' is a structure previously initialized by WebPInitDecoderConfig().
|
||||||
|
// 'config->output' should have the desired colorspace selected.
|
||||||
|
// Returns the decoder status. On success 'config->output' will contain the
|
||||||
|
// decoded picture.
|
||||||
|
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
|
||||||
|
WebPDecoderConfig* const config);
|
||||||
|
|
||||||
|
// Same as DecodeWebP(), but using the incremental decoder.
|
||||||
|
VP8StatusCode DecodeWebPIncremental(
|
||||||
|
const uint8_t* const data, size_t data_size,
|
||||||
|
WebPDecoderConfig* const config);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Reads a WebP from 'in_file', returning the decoded output in 'pic'.
|
||||||
|
// Output is RGBA or YUVA, depending on pic->use_argb value.
|
||||||
|
// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA
|
||||||
|
// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
|
||||||
|
// Returns true on success.
|
||||||
|
int ReadWebP(const uint8_t* const data, size_t data_size,
|
||||||
|
struct WebPPicture* const pic,
|
||||||
|
int keep_alpha, struct Metadata* const metadata);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBP_IMAGEIO_WEBPDEC_H_
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
#include "webp/config.h"
|
#include "webp/config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef HAVE_WINCODEC_H
|
#ifdef HAVE_WINCODEC_H
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
@@ -25,11 +27,13 @@
|
|||||||
#define COBJMACROS
|
#define COBJMACROS
|
||||||
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
||||||
// code with COBJMACROS.
|
// code with COBJMACROS.
|
||||||
|
#include <ole2.h> // CreateStreamOnHGlobal()
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wincodec.h>
|
#include <wincodec.h>
|
||||||
|
|
||||||
#include "webp/encode.h"
|
#include "webp/encode.h"
|
||||||
|
#include "./imageio_util.h"
|
||||||
#include "./metadata.h"
|
#include "./metadata.h"
|
||||||
|
|
||||||
#define IFS(fn) \
|
#define IFS(fn) \
|
||||||
@@ -72,10 +76,41 @@ WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
|
|||||||
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
||||||
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
||||||
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
||||||
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
|
||||||
|
0x1562ff7c, 0xd352, 0x46f9,
|
||||||
|
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
|
||||||
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
||||||
|
0x6fddc324, 0x4e03, 0x4bfe,
|
||||||
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
|
||||||
|
|
||||||
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
if (!strcmp(filename, "-")) {
|
||||||
|
const uint8_t* data = NULL;
|
||||||
|
size_t data_size = 0;
|
||||||
|
const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
|
||||||
|
if (ok) {
|
||||||
|
HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
|
||||||
|
if (image != NULL) {
|
||||||
|
void* const image_mem = GlobalLock(image);
|
||||||
|
if (image_mem != NULL) {
|
||||||
|
memcpy(image_mem, data, data_size);
|
||||||
|
GlobalUnlock(image);
|
||||||
|
IFS(CreateStreamOnHGlobal(image, TRUE, stream));
|
||||||
|
} else {
|
||||||
|
hr = E_FAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
free((void*)data);
|
||||||
|
} else {
|
||||||
|
hr = E_FAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
|
IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
|
||||||
|
}
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
|
fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
|
||||||
}
|
}
|
||||||
@@ -109,6 +144,7 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
|||||||
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
||||||
count, color_contexts,
|
count, color_contexts,
|
||||||
&num_color_contexts));
|
&num_color_contexts));
|
||||||
|
assert(FAILED(hr) || num_color_contexts <= count);
|
||||||
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
||||||
WICColorContextType type;
|
WICColorContextType type;
|
||||||
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
||||||
@@ -116,7 +152,7 @@ static HRESULT ExtractICCP(IWICImagingFactory* const factory,
|
|||||||
UINT size;
|
UINT size;
|
||||||
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
||||||
0, NULL, &size));
|
0, NULL, &size));
|
||||||
if (size > 0) {
|
if (SUCCEEDED(hr) && size > 0) {
|
||||||
iccp->bytes = (uint8_t*)malloc(size);
|
iccp->bytes = (uint8_t*)malloc(size);
|
||||||
if (iccp->bytes == NULL) {
|
if (iccp->bytes == NULL) {
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
@@ -194,7 +230,11 @@ static int HasAlpha(IWICImagingFactory* const factory,
|
|||||||
has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
|
has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||||
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
|
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
|
||||||
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||||
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_));
|
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
|
||||||
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||||
|
MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
|
||||||
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
||||||
|
MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
|
||||||
}
|
}
|
||||||
return has_alpha;
|
return has_alpha;
|
||||||
}
|
}
|
||||||
@@ -234,7 +274,9 @@ int ReadPictureWithWIC(const char* const filename,
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
int has_alpha = 0;
|
int has_alpha = 0;
|
||||||
int stride;
|
int64_t stride;
|
||||||
|
|
||||||
|
if (filename == NULL || pic == NULL) return 0;
|
||||||
|
|
||||||
IFS(CoInitialize(NULL));
|
IFS(CoInitialize(NULL));
|
||||||
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
||||||
@@ -261,7 +303,7 @@ int ReadPictureWithWIC(const char* const filename,
|
|||||||
IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
|
IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
|
||||||
IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
|
IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
|
||||||
|
|
||||||
if (keep_alpha) {
|
if (SUCCEEDED(hr) && keep_alpha) {
|
||||||
const GUID** guid;
|
const GUID** guid;
|
||||||
for (guid = kAlphaContainers; *guid != NULL; ++guid) {
|
for (guid = kAlphaContainers; *guid != NULL; ++guid) {
|
||||||
if (IsEqualGUID(MAKE_REFGUID(src_container_format),
|
if (IsEqualGUID(MAKE_REFGUID(src_container_format),
|
||||||
@@ -294,22 +336,27 @@ int ReadPictureWithWIC(const char* const filename,
|
|||||||
|
|
||||||
// Decode.
|
// Decode.
|
||||||
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
||||||
stride = importer->bytes_per_pixel * width * sizeof(*rgb);
|
stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
|
||||||
|
if (stride != (int)stride ||
|
||||||
|
!ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
|
||||||
|
hr = E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
rgb = (BYTE*)malloc(stride * height);
|
rgb = (BYTE*)malloc((size_t)stride * height);
|
||||||
if (rgb == NULL)
|
if (rgb == NULL)
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
}
|
}
|
||||||
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
||||||
stride, stride * height, rgb));
|
(UINT)stride, (UINT)stride * height, rgb));
|
||||||
|
|
||||||
// WebP conversion.
|
// WebP conversion.
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
int ok;
|
int ok;
|
||||||
pic->width = width;
|
pic->width = width;
|
||||||
pic->height = height;
|
pic->height = height;
|
||||||
pic->use_argb = 1;
|
pic->use_argb = 1; // For WIC, we always force to argb
|
||||||
ok = importer->import(pic, rgb, stride);
|
ok = importer->import(pic, rgb, (int)stride);
|
||||||
if (!ok) hr = E_FAIL;
|
if (!ok) hr = E_FAIL;
|
||||||
}
|
}
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
//
|
//
|
||||||
// Windows Imaging Component (WIC) decode.
|
// Windows Imaging Component (WIC) decode.
|
||||||
|
|
||||||
#ifndef WEBP_EXAMPLES_WICDEC_H_
|
#ifndef WEBP_IMAGEIO_WICDEC_H_
|
||||||
#define WEBP_EXAMPLES_WICDEC_H_
|
#define WEBP_IMAGEIO_WICDEC_H_
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -21,7 +21,7 @@ struct WebPPicture;
|
|||||||
|
|
||||||
// Reads an image from 'filename', returning the decoded output in 'pic'.
|
// Reads an image from 'filename', returning the decoded output in 'pic'.
|
||||||
// If 'keep_alpha' is true and the image has an alpha channel, the output is
|
// If 'keep_alpha' is true and the image has an alpha channel, the output is
|
||||||
// RGBA otherwise it will be RGB.
|
// RGBA otherwise it will be RGB. pic->use_argb is always forced to true.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
int ReadPictureWithWIC(const char* const filename,
|
int ReadPictureWithWIC(const char* const filename,
|
||||||
struct WebPPicture* const pic, int keep_alpha,
|
struct WebPPicture* const pic, int keep_alpha,
|
||||||
@@ -31,4 +31,4 @@ int ReadPictureWithWIC(const char* const filename,
|
|||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WEBP_EXAMPLES_WICDEC_H_
|
#endif // WEBP_IMAGEIO_WICDEC_H_
|
||||||
102
iosbuild.sh
102
iosbuild.sh
@@ -1,10 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# This script generates 'WebP.framework'. An iOS app can decode WebP images
|
# This script generates 'WebP.framework' and 'WebPDecoder.framework'. An iOS
|
||||||
# by including 'WebP.framework'.
|
# app can decode WebP images by including 'WebPDecoder.framework' and both
|
||||||
|
# encode and decode WebP images by including 'WebP.framework'.
|
||||||
#
|
#
|
||||||
# Run ./iosbuild.sh to generate 'WebP.framework' under the current directory
|
# Run ./iosbuild.sh to generate the frameworks under the current directory
|
||||||
# (previous build will be erased if it exists).
|
# (the previous build will be erased if it exists).
|
||||||
#
|
#
|
||||||
# This script is inspired by the build script written by Carson McDonald.
|
# This script is inspired by the build script written by Carson McDonald.
|
||||||
# (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
|
# (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
|
||||||
@@ -12,42 +13,60 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Extract the latest SDK version from the final field of the form: iphoneosX.Y
|
# Extract the latest SDK version from the final field of the form: iphoneosX.Y
|
||||||
declare -r SDK=$(xcodebuild -showsdks \
|
readonly SDK=$(xcodebuild -showsdks \
|
||||||
| grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
|
| grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
|
||||||
)
|
)
|
||||||
# Extract Xcode version.
|
# Extract Xcode version.
|
||||||
declare -r XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
|
readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
|
||||||
|
if [[ -z "${XCODE}" ]]; then
|
||||||
|
echo "Xcode not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
declare -r OLDPATH=${PATH}
|
readonly OLDPATH=${PATH}
|
||||||
|
|
||||||
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
|
# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
|
||||||
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
|
# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
|
||||||
declare -r PLATFORMS="iPhoneSimulator iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
|
PLATFORMS="iPhoneSimulator iPhoneSimulator64"
|
||||||
declare -r SRCDIR=$(dirname $0)
|
PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
|
||||||
declare -r TOPDIR=$(pwd)
|
readonly PLATFORMS
|
||||||
declare -r BUILDDIR="${TOPDIR}/iosbuild"
|
readonly SRCDIR=$(dirname $0)
|
||||||
declare -r TARGETDIR="${TOPDIR}/WebP.framework"
|
readonly TOPDIR=$(pwd)
|
||||||
declare -r DEVELOPER=$(xcode-select --print-path)
|
readonly BUILDDIR="${TOPDIR}/iosbuild"
|
||||||
declare -r PLATFORMSROOT="${DEVELOPER}/Platforms"
|
readonly TARGETDIR="${TOPDIR}/WebP.framework"
|
||||||
declare -r LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
|
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework"
|
||||||
|
readonly DEVELOPER=$(xcode-select --print-path)
|
||||||
|
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
|
||||||
|
readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
|
||||||
LIBLIST=''
|
LIBLIST=''
|
||||||
|
DECLIBLIST=''
|
||||||
|
|
||||||
if [[ -z "${SDK}" ]]; then
|
if [[ -z "${SDK}" ]]; then
|
||||||
echo "iOS SDK not available"
|
echo "iOS SDK not available"
|
||||||
exit 1
|
exit 1
|
||||||
elif [[ ${SDK} < 4.0 ]]; then
|
elif [[ ${SDK%%.*} -gt 8 ]]; then
|
||||||
echo "You need iOS SDK version 4.0 or above"
|
EXTRA_CFLAGS="-fembed-bitcode"
|
||||||
|
elif [[ ${SDK} < 6.0 ]]; then
|
||||||
|
echo "You need iOS SDK version 6.0 or above"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
echo "iOS SDK Version ${SDK}"
|
echo "iOS SDK Version ${SDK}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf ${BUILDDIR}
|
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR}
|
||||||
rm -rf ${TARGETDIR}
|
mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/
|
||||||
mkdir -p ${BUILDDIR}
|
|
||||||
mkdir -p ${TARGETDIR}/Headers/
|
|
||||||
|
|
||||||
[[ -e ${SRCDIR}/configure ]] || (cd ${SRCDIR} && sh autogen.sh)
|
if [[ ! -e ${SRCDIR}/configure ]]; then
|
||||||
|
if ! (cd ${SRCDIR} && sh autogen.sh); then
|
||||||
|
cat <<EOT
|
||||||
|
Error creating configure script!
|
||||||
|
This script requires the autoconf/automake and libtool to build. MacPorts can
|
||||||
|
be used to obtain these:
|
||||||
|
http://www.macports.org/install.php
|
||||||
|
EOT
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
for PLATFORM in ${PLATFORMS}; do
|
for PLATFORM in ${PLATFORMS}; do
|
||||||
ARCH2=""
|
ARCH2=""
|
||||||
@@ -64,6 +83,9 @@ for PLATFORM in ${PLATFORMS}; do
|
|||||||
elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then
|
elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then
|
||||||
PLATFORM="iPhoneOS"
|
PLATFORM="iPhoneOS"
|
||||||
ARCH="armv6"
|
ARCH="armv6"
|
||||||
|
elif [[ "${PLATFORM}" == "iPhoneSimulator64" ]]; then
|
||||||
|
PLATFORM="iPhoneSimulator"
|
||||||
|
ARCH="x86_64"
|
||||||
else
|
else
|
||||||
ARCH="i386"
|
ARCH="i386"
|
||||||
fi
|
fi
|
||||||
@@ -71,37 +93,28 @@ for PLATFORM in ${PLATFORMS}; do
|
|||||||
ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
|
ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
|
||||||
mkdir -p "${ROOTDIR}"
|
mkdir -p "${ROOTDIR}"
|
||||||
|
|
||||||
SDKROOT="${PLATFORMSROOT}/${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
|
|
||||||
CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
|
|
||||||
LDFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT}"
|
|
||||||
|
|
||||||
if [[ -z "${XCODE}" ]]; then
|
|
||||||
echo "XCODE not available"
|
|
||||||
exit 1
|
|
||||||
elif [[ ${SDK} < 5.0.0 ]]; then
|
|
||||||
DEVROOT="${PLATFORMSROOT}/${PLATFORM}.platform/Developer/"
|
|
||||||
else
|
|
||||||
DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
|
DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
|
||||||
CFLAGS+=" -miphoneos-version-min=5.0"
|
SDKROOT="${PLATFORMSROOT}/"
|
||||||
LDFLAGS+=" -miphoneos-version-min=5.0"
|
SDKROOT+="${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
|
||||||
fi
|
CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
|
||||||
|
CFLAGS+=" -miphoneos-version-min=6.0 ${EXTRA_CFLAGS}"
|
||||||
|
|
||||||
export CFLAGS
|
set -x
|
||||||
export LDFLAGS
|
|
||||||
export CXXFLAGS=${CFLAGS}
|
|
||||||
export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
|
export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
|
||||||
|
|
||||||
${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \
|
${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \
|
||||||
--build=$(${SRCDIR}/config.guess) \
|
--build=$(${SRCDIR}/config.guess) \
|
||||||
--disable-shared --enable-static \
|
--disable-shared --enable-static \
|
||||||
--enable-libwebpdecoder --enable-swap-16bit-csp
|
--enable-libwebpdecoder --enable-swap-16bit-csp \
|
||||||
|
CFLAGS="${CFLAGS}"
|
||||||
|
set +x
|
||||||
|
|
||||||
# run make only in the src/ directory to create libwebpdecoder.a
|
# run make only in the src/ directory to create libwebp.a/libwebpdecoder.a
|
||||||
cd src/
|
cd src/
|
||||||
make V=0
|
make V=0
|
||||||
make install
|
make install
|
||||||
|
|
||||||
LIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
|
LIBLIST+=" ${ROOTDIR}/lib/libwebp.a"
|
||||||
|
DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
|
||||||
|
|
||||||
make clean
|
make clean
|
||||||
cd ..
|
cd ..
|
||||||
@@ -109,5 +122,8 @@ for PLATFORM in ${PLATFORMS}; do
|
|||||||
export PATH=${OLDPATH}
|
export PATH=${OLDPATH}
|
||||||
done
|
done
|
||||||
|
|
||||||
cp -a ${SRCDIR}/src/webp/* ${TARGETDIR}/Headers/
|
cp -a ${SRCDIR}/src/webp/{decode,encode,types}.h ${TARGETDIR}/Headers/
|
||||||
${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
|
${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
|
||||||
|
|
||||||
|
cp -a ${SRCDIR}/src/webp/{decode,types}.h ${DECTARGETDIR}/Headers/
|
||||||
|
${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder
|
||||||
|
|||||||
343
makefile.unix
343
makefile.unix
@@ -2,8 +2,8 @@
|
|||||||
# system, for simple local building of the libraries and tools.
|
# system, for simple local building of the libraries and tools.
|
||||||
# It will not install the libraries system-wide, but just create the 'cwebp'
|
# It will not install the libraries system-wide, but just create the 'cwebp'
|
||||||
# and 'dwebp' tools in the examples/ directory, along with the static
|
# and 'dwebp' tools in the examples/ directory, along with the static
|
||||||
# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a' and
|
# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a',
|
||||||
# 'src/demux/libwebpdemux.a'.
|
# 'src/demux/libwebpdemux.a' and 'extras/libwebpextras.a'.
|
||||||
#
|
#
|
||||||
# To build the library and examples, use:
|
# To build the library and examples, use:
|
||||||
# make -f makefile.unix
|
# make -f makefile.unix
|
||||||
@@ -61,14 +61,24 @@ endif
|
|||||||
EXTRA_FLAGS += -DWEBP_USE_THREAD
|
EXTRA_FLAGS += -DWEBP_USE_THREAD
|
||||||
EXTRA_LIBS += -lpthread
|
EXTRA_LIBS += -lpthread
|
||||||
|
|
||||||
|
# Control symbol visibility. Comment out if your compiler doesn't support it.
|
||||||
|
EXTRA_FLAGS += -fvisibility=hidden
|
||||||
|
|
||||||
# Extra flags to emulate C89 strictness with the full ANSI
|
# Extra flags to emulate C89 strictness with the full ANSI
|
||||||
EXTRA_FLAGS += -Wextra -Wold-style-definition
|
EXTRA_FLAGS += -Wextra -Wold-style-definition
|
||||||
EXTRA_FLAGS += -Wmissing-prototypes
|
EXTRA_FLAGS += -Wmissing-prototypes
|
||||||
EXTRA_FLAGS += -Wmissing-declarations
|
EXTRA_FLAGS += -Wmissing-declarations
|
||||||
EXTRA_FLAGS += -Wdeclaration-after-statement
|
EXTRA_FLAGS += -Wdeclaration-after-statement
|
||||||
EXTRA_FLAGS += -Wshadow
|
EXTRA_FLAGS += -Wshadow
|
||||||
|
EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral
|
||||||
# EXTRA_FLAGS += -Wvla
|
# EXTRA_FLAGS += -Wvla
|
||||||
|
|
||||||
|
# SSE4.1-specific flags:
|
||||||
|
ifeq ($(HAVE_SSE41), 1)
|
||||||
|
EXTRA_FLAGS += -DWEBP_HAVE_SSE41
|
||||||
|
src/dsp/%_sse41.o: EXTRA_FLAGS += -msse4.1
|
||||||
|
endif
|
||||||
|
|
||||||
# AVX2-specific flags:
|
# AVX2-specific flags:
|
||||||
ifeq ($(HAVE_AVX2), 1)
|
ifeq ($(HAVE_AVX2), 1)
|
||||||
EXTRA_FLAGS += -DWEBP_HAVE_AVX2
|
EXTRA_FLAGS += -DWEBP_HAVE_AVX2
|
||||||
@@ -79,120 +89,187 @@ endif
|
|||||||
# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8
|
# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8
|
||||||
# -> seems to make the overall lib slower: -fno-split-wide-types
|
# -> seems to make the overall lib slower: -fno-split-wide-types
|
||||||
|
|
||||||
|
# MIPS (MSA) 32-bit build specific flags for mips32r5 (p5600):
|
||||||
|
# EXTRA_FLAGS += -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64
|
||||||
|
# EXTRA_FLAGS += -msched-weight -mload-store-pairs
|
||||||
|
|
||||||
|
# MIPS (MSA) 64-bit build specific flags for mips64r6 (i6400):
|
||||||
|
# EXTRA_FLAGS += -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64
|
||||||
|
# EXTRA_FLAGS += -msched-weight -mload-store-pairs
|
||||||
|
|
||||||
#### Nothing should normally be changed below this line ####
|
#### Nothing should normally be changed below this line ####
|
||||||
|
|
||||||
AR = ar
|
AR = ar
|
||||||
ARFLAGS = r
|
ARFLAGS = r
|
||||||
CC = gcc
|
|
||||||
CPPFLAGS = -Isrc/ -Wall
|
CPPFLAGS = -Isrc/ -Wall
|
||||||
CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS)
|
CFLAGS = -O3 -DNDEBUG $(EXTRA_FLAGS)
|
||||||
|
CC = gcc
|
||||||
INSTALL = install
|
INSTALL = install
|
||||||
GROFF = /usr/bin/groff
|
GROFF = /usr/bin/groff
|
||||||
COL = /usr/bin/col
|
COL = /usr/bin/col
|
||||||
LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm
|
LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm
|
||||||
|
|
||||||
|
ANIM_UTIL_OBJS = \
|
||||||
|
examples/anim_util.o \
|
||||||
|
|
||||||
DEC_OBJS = \
|
DEC_OBJS = \
|
||||||
src/dec/alpha.o \
|
src/dec/alpha_dec.o \
|
||||||
src/dec/buffer.o \
|
src/dec/buffer_dec.o \
|
||||||
src/dec/frame.o \
|
src/dec/frame_dec.o \
|
||||||
src/dec/idec.o \
|
src/dec/idec_dec.o \
|
||||||
src/dec/io.o \
|
src/dec/io_dec.o \
|
||||||
src/dec/quant.o \
|
src/dec/quant_dec.o \
|
||||||
src/dec/tree.o \
|
src/dec/tree_dec.o \
|
||||||
src/dec/vp8.o \
|
src/dec/vp8_dec.o \
|
||||||
src/dec/vp8l.o \
|
src/dec/vp8l_dec.o \
|
||||||
src/dec/webp.o \
|
src/dec/webp_dec.o \
|
||||||
|
|
||||||
DEMUX_OBJS = \
|
DEMUX_OBJS = \
|
||||||
|
src/demux/anim_decode.o \
|
||||||
src/demux/demux.o \
|
src/demux/demux.o \
|
||||||
|
|
||||||
DSP_DEC_OBJS = \
|
DSP_DEC_OBJS = \
|
||||||
src/dsp/alpha_processing.o \
|
src/dsp/alpha_processing.o \
|
||||||
|
src/dsp/alpha_processing_mips_dsp_r2.o \
|
||||||
|
src/dsp/alpha_processing_neon.o \
|
||||||
|
src/dsp/alpha_processing_sse2.o \
|
||||||
|
src/dsp/alpha_processing_sse41.o \
|
||||||
src/dsp/cpu.o \
|
src/dsp/cpu.o \
|
||||||
src/dsp/dec.o \
|
src/dsp/dec.o \
|
||||||
src/dsp/dec_clip_tables.o \
|
src/dsp/dec_clip_tables.o \
|
||||||
src/dsp/dec_mips32.o \
|
src/dsp/dec_mips32.o \
|
||||||
|
src/dsp/dec_mips_dsp_r2.o \
|
||||||
|
src/dsp/dec_msa.o \
|
||||||
src/dsp/dec_neon.o \
|
src/dsp/dec_neon.o \
|
||||||
src/dsp/dec_sse2.o \
|
src/dsp/dec_sse2.o \
|
||||||
|
src/dsp/dec_sse41.o \
|
||||||
|
src/dsp/filters.o \
|
||||||
|
src/dsp/filters_mips_dsp_r2.o \
|
||||||
|
src/dsp/filters_msa.o \
|
||||||
|
src/dsp/filters_neon.o \
|
||||||
|
src/dsp/filters_sse2.o \
|
||||||
src/dsp/lossless.o \
|
src/dsp/lossless.o \
|
||||||
src/dsp/lossless_mips32.o \
|
src/dsp/lossless_mips_dsp_r2.o \
|
||||||
|
src/dsp/lossless_msa.o \
|
||||||
src/dsp/lossless_neon.o \
|
src/dsp/lossless_neon.o \
|
||||||
src/dsp/lossless_sse2.o \
|
src/dsp/lossless_sse2.o \
|
||||||
|
src/dsp/rescaler.o \
|
||||||
|
src/dsp/rescaler_mips32.o \
|
||||||
|
src/dsp/rescaler_mips_dsp_r2.o \
|
||||||
|
src/dsp/rescaler_msa.o \
|
||||||
|
src/dsp/rescaler_neon.o \
|
||||||
|
src/dsp/rescaler_sse2.o \
|
||||||
src/dsp/upsampling.o \
|
src/dsp/upsampling.o \
|
||||||
|
src/dsp/upsampling_mips_dsp_r2.o \
|
||||||
|
src/dsp/upsampling_msa.o \
|
||||||
src/dsp/upsampling_neon.o \
|
src/dsp/upsampling_neon.o \
|
||||||
src/dsp/upsampling_sse2.o \
|
src/dsp/upsampling_sse2.o \
|
||||||
src/dsp/yuv.o \
|
src/dsp/yuv.o \
|
||||||
src/dsp/yuv_mips32.o \
|
src/dsp/yuv_mips32.o \
|
||||||
|
src/dsp/yuv_mips_dsp_r2.o \
|
||||||
src/dsp/yuv_sse2.o \
|
src/dsp/yuv_sse2.o \
|
||||||
|
|
||||||
DSP_ENC_OBJS = \
|
DSP_ENC_OBJS = \
|
||||||
|
src/dsp/argb.o \
|
||||||
|
src/dsp/argb_mips_dsp_r2.o \
|
||||||
|
src/dsp/argb_sse2.o \
|
||||||
|
src/dsp/cost.o \
|
||||||
|
src/dsp/cost_mips32.o \
|
||||||
|
src/dsp/cost_mips_dsp_r2.o \
|
||||||
|
src/dsp/cost_sse2.o \
|
||||||
src/dsp/enc.o \
|
src/dsp/enc.o \
|
||||||
src/dsp/enc_avx2.o \
|
src/dsp/enc_avx2.o \
|
||||||
src/dsp/enc_mips32.o \
|
src/dsp/enc_mips32.o \
|
||||||
|
src/dsp/enc_mips_dsp_r2.o \
|
||||||
|
src/dsp/enc_msa.o \
|
||||||
src/dsp/enc_neon.o \
|
src/dsp/enc_neon.o \
|
||||||
src/dsp/enc_sse2.o \
|
src/dsp/enc_sse2.o \
|
||||||
|
src/dsp/enc_sse41.o \
|
||||||
|
src/dsp/lossless_enc.o \
|
||||||
|
src/dsp/lossless_enc_mips32.o \
|
||||||
|
src/dsp/lossless_enc_mips_dsp_r2.o \
|
||||||
|
src/dsp/lossless_enc_msa.o \
|
||||||
|
src/dsp/lossless_enc_neon.o \
|
||||||
|
src/dsp/lossless_enc_sse2.o \
|
||||||
|
src/dsp/lossless_enc_sse41.o \
|
||||||
|
|
||||||
ENC_OBJS = \
|
ENC_OBJS = \
|
||||||
src/enc/alpha.o \
|
src/enc/alpha_enc.o \
|
||||||
src/enc/analysis.o \
|
src/enc/analysis_enc.o \
|
||||||
src/enc/backward_references.o \
|
src/enc/backward_references_enc.o \
|
||||||
src/enc/config.o \
|
src/enc/config_enc.o \
|
||||||
src/enc/cost.o \
|
src/enc/cost_enc.o \
|
||||||
src/enc/filter.o \
|
src/enc/delta_palettization_enc.o \
|
||||||
src/enc/frame.o \
|
src/enc/filter_enc.o \
|
||||||
src/enc/histogram.o \
|
src/enc/frame_enc.o \
|
||||||
src/enc/iterator.o \
|
src/enc/histogram_enc.o \
|
||||||
src/enc/picture.o \
|
src/enc/iterator_enc.o \
|
||||||
src/enc/picture_csp.o \
|
src/enc/near_lossless_enc.o \
|
||||||
src/enc/picture_psnr.o \
|
src/enc/picture_enc.o \
|
||||||
src/enc/picture_rescale.o \
|
src/enc/picture_csp_enc.o \
|
||||||
src/enc/picture_tools.o \
|
src/enc/picture_psnr_enc.o \
|
||||||
src/enc/quant.o \
|
src/enc/picture_rescale_enc.o \
|
||||||
src/enc/syntax.o \
|
src/enc/picture_tools_enc.o \
|
||||||
src/enc/token.o \
|
src/enc/predictor_enc.o \
|
||||||
src/enc/tree.o \
|
src/enc/quant_enc.o \
|
||||||
src/enc/vp8l.o \
|
src/enc/syntax_enc.o \
|
||||||
src/enc/webpenc.o \
|
src/enc/token_enc.o \
|
||||||
|
src/enc/tree_enc.o \
|
||||||
|
src/enc/vp8l_enc.o \
|
||||||
|
src/enc/webp_enc.o \
|
||||||
|
|
||||||
EX_FORMAT_DEC_OBJS = \
|
EX_FORMAT_DEC_OBJS = \
|
||||||
examples/jpegdec.o \
|
imageio/image_dec.o \
|
||||||
examples/metadata.o \
|
imageio/jpegdec.o \
|
||||||
examples/pngdec.o \
|
imageio/metadata.o \
|
||||||
examples/tiffdec.o \
|
imageio/pngdec.o \
|
||||||
examples/webpdec.o \
|
imageio/tiffdec.o \
|
||||||
|
imageio/webpdec.o \
|
||||||
|
|
||||||
|
EX_FORMAT_ENC_OBJS = \
|
||||||
|
imageio/image_enc.o \
|
||||||
|
|
||||||
EX_UTIL_OBJS = \
|
EX_UTIL_OBJS = \
|
||||||
examples/example_util.o \
|
examples/example_util.o \
|
||||||
|
|
||||||
GIF2WEBP_UTIL_OBJS = \
|
GIFDEC_OBJS = \
|
||||||
examples/gif2webp_util.o \
|
examples/gifdec.o \
|
||||||
|
|
||||||
|
IMAGE_UTIL_OBJS = \
|
||||||
|
imageio/imageio_util.o \
|
||||||
|
|
||||||
MUX_OBJS = \
|
MUX_OBJS = \
|
||||||
|
src/mux/anim_encode.o \
|
||||||
src/mux/muxedit.o \
|
src/mux/muxedit.o \
|
||||||
src/mux/muxinternal.o \
|
src/mux/muxinternal.o \
|
||||||
src/mux/muxread.o \
|
src/mux/muxread.o \
|
||||||
|
|
||||||
UTILS_DEC_OBJS = \
|
UTILS_DEC_OBJS = \
|
||||||
src/utils/bit_reader.o \
|
src/utils/bit_reader_utils.o \
|
||||||
src/utils/color_cache.o \
|
src/utils/color_cache_utils.o \
|
||||||
src/utils/filters.o \
|
src/utils/filters_utils.o \
|
||||||
src/utils/huffman.o \
|
src/utils/huffman_utils.o \
|
||||||
src/utils/quant_levels_dec.o \
|
src/utils/quant_levels_dec_utils.o \
|
||||||
src/utils/random.o \
|
src/utils/random_utils.o \
|
||||||
src/utils/rescaler.o \
|
src/utils/rescaler_utils.o \
|
||||||
src/utils/thread.o \
|
src/utils/thread_utils.o \
|
||||||
src/utils/utils.o \
|
src/utils/utils.o \
|
||||||
|
|
||||||
UTILS_ENC_OBJS = \
|
UTILS_ENC_OBJS = \
|
||||||
src/utils/bit_writer.o \
|
src/utils/bit_writer_utils.o \
|
||||||
src/utils/huffman_encode.o \
|
src/utils/huffman_encode_utils.o \
|
||||||
src/utils/quant_levels.o \
|
src/utils/quant_levels_utils.o \
|
||||||
|
|
||||||
|
EXTRA_OBJS = \
|
||||||
|
extras/extras.o \
|
||||||
|
extras/quality_estimate.o \
|
||||||
|
|
||||||
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
|
LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
|
||||||
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
|
LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
|
||||||
$(UTILS_ENC_OBJS)
|
$(UTILS_ENC_OBJS)
|
||||||
LIBWEBPMUX_OBJS = $(MUX_OBJS)
|
LIBWEBPMUX_OBJS = $(MUX_OBJS)
|
||||||
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
|
LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
|
||||||
|
LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS)
|
||||||
|
|
||||||
HDRS_INSTALLED = \
|
HDRS_INSTALLED = \
|
||||||
src/webp/decode.h \
|
src/webp/decode.h \
|
||||||
@@ -203,66 +280,91 @@ HDRS_INSTALLED = \
|
|||||||
src/webp/types.h \
|
src/webp/types.h \
|
||||||
|
|
||||||
HDRS = \
|
HDRS = \
|
||||||
src/dec/alphai.h \
|
src/dec/alphai_dec.h \
|
||||||
src/dec/decode_vp8.h \
|
src/dec/common_dec.h \
|
||||||
src/dec/vp8i.h \
|
src/dec/vp8_dec.h \
|
||||||
src/dec/vp8li.h \
|
src/dec/vp8i_dec.h \
|
||||||
src/dec/webpi.h \
|
src/dec/vp8li_dec.h \
|
||||||
|
src/dec/webpi_dec.h \
|
||||||
|
src/dsp/common_sse2.h \
|
||||||
src/dsp/dsp.h \
|
src/dsp/dsp.h \
|
||||||
src/dsp/lossless.h \
|
src/dsp/lossless.h \
|
||||||
|
src/dsp/lossless_common.h \
|
||||||
|
src/dsp/mips_macro.h \
|
||||||
|
src/dsp/msa_macro.h \
|
||||||
src/dsp/neon.h \
|
src/dsp/neon.h \
|
||||||
src/dsp/yuv.h \
|
src/dsp/yuv.h \
|
||||||
src/dsp/yuv_tables_sse2.h \
|
src/enc/backward_references_enc.h \
|
||||||
src/enc/backward_references.h \
|
src/enc/cost_enc.h \
|
||||||
src/enc/cost.h \
|
src/enc/delta_palettization_enc.h \
|
||||||
src/enc/histogram.h \
|
src/enc/histogram_enc.h \
|
||||||
src/enc/vp8enci.h \
|
src/enc/vp8i_enc.h \
|
||||||
src/enc/vp8li.h \
|
src/enc/vp8li_enc.h \
|
||||||
|
src/mux/animi.h \
|
||||||
src/mux/muxi.h \
|
src/mux/muxi.h \
|
||||||
src/utils/bit_reader.h \
|
src/utils/bit_reader_utils.h \
|
||||||
src/utils/bit_writer.h \
|
src/utils/bit_reader_inl_utils.h \
|
||||||
src/utils/color_cache.h \
|
src/utils/bit_writer_utils.h \
|
||||||
src/utils/filters.h \
|
src/utils/color_cache_utils.h \
|
||||||
src/utils/huffman.h \
|
src/utils/endian_inl_utils.h \
|
||||||
src/utils/huffman_encode.h \
|
src/utils/filters_utils.h \
|
||||||
src/utils/quant_levels.h \
|
src/utils/huffman_utils.h \
|
||||||
src/utils/quant_levels_dec.h \
|
src/utils/huffman_encode_utils.h \
|
||||||
src/utils/random.h \
|
src/utils/quant_levels_utils.h \
|
||||||
src/utils/rescaler.h \
|
src/utils/quant_levels_dec_utils.h \
|
||||||
src/utils/thread.h \
|
src/utils/random_utils.h \
|
||||||
|
src/utils/rescaler_utils.h \
|
||||||
|
src/utils/thread_utils.h \
|
||||||
src/utils/utils.h \
|
src/utils/utils.h \
|
||||||
src/webp/format_constants.h \
|
src/webp/format_constants.h \
|
||||||
$(HDRS_INSTALLED) \
|
$(HDRS_INSTALLED) \
|
||||||
|
|
||||||
OUT_LIBS = examples/libexample_util.a src/libwebpdecoder.a src/libwebp.a
|
OUT_LIBS = examples/libexample_util.a
|
||||||
|
OUT_LIBS += imageio/libimageio_util.a
|
||||||
|
OUT_LIBS += imageio/libimagedec.a
|
||||||
|
OUT_LIBS += imageio/libimageenc.a
|
||||||
|
OUT_LIBS += src/libwebpdecoder.a
|
||||||
|
OUT_LIBS += src/libwebp.a
|
||||||
|
EXTRA_LIB = extras/libwebpextras.a
|
||||||
OUT_EXAMPLES = examples/cwebp examples/dwebp
|
OUT_EXAMPLES = examples/cwebp examples/dwebp
|
||||||
EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux
|
EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \
|
||||||
|
examples/anim_diff examples/img2webp
|
||||||
|
OTHER_EXAMPLES = extras/get_disto extras/webp_quality
|
||||||
|
|
||||||
OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
|
OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
|
||||||
ifeq ($(MAKECMDGOALS),clean)
|
ifeq ($(MAKECMDGOALS),clean)
|
||||||
OUTPUT += $(EXTRA_EXAMPLES)
|
OUTPUT += $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
|
||||||
OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a
|
OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB)
|
||||||
OUTPUT += examples/libgif2webp_util.a
|
OUTPUT += examples/libgifdec.a examples/libanim_util.a
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ex: $(OUT_EXAMPLES)
|
ex: $(OUT_EXAMPLES)
|
||||||
all: ex $(EXTRA_EXAMPLES)
|
all: ex $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
|
||||||
|
extras: $(EXTRA_LIB)
|
||||||
|
|
||||||
$(EX_FORMAT_DEC_OBJS): %.o: %.h
|
$(EX_FORMAT_DEC_OBJS): %.o: %.h
|
||||||
|
|
||||||
# special dependencies:
|
# special dependencies:
|
||||||
# tree.c/vp8.c/bit_reader.c <-> bit_reader_inl.h, endian_inl.h
|
# tree_dec.c/vp8_dec.c/bit_reader_utils.c <->
|
||||||
# bit_writer.c <-> endian_inl.h
|
# bit_reader_inl_utils.h, endian_inl_utils.h
|
||||||
src/dec/tree.o: src/utils/bit_reader_inl.h src/utils/endian_inl.h
|
# bit_writer_utils.c <-> endian_inl_utils.h
|
||||||
src/dec/vp8.o: src/utils/bit_reader_inl.h src/utils/endian_inl.h
|
src/dec/tree_dec.o: src/utils/bit_reader_inl_utils.h
|
||||||
src/utils/bit_reader.o: src/utils/bit_reader_inl.h src/utils/endian_inl.h
|
src/dec/tree_dec.o: src/utils/endian_inl_utils.h
|
||||||
src/utils/bit_writer.o: src/utils/endian_inl.h
|
src/dec/vp8_dec.o: src/utils/bit_reader_inl_utils.h src/utils/endian_inl_utils.h
|
||||||
|
src/utils/bit_reader_utils.o: src/utils/bit_reader_inl_utils.h
|
||||||
|
src/utils/bit_reader_utils.o: src/utils/endian_inl_utils.h
|
||||||
|
src/utils/bit_writer_utils.o: src/utils/endian_inl_utils.h
|
||||||
|
|
||||||
%.o: %.c $(HDRS)
|
%.o: %.c $(HDRS)
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
examples/libanim_util.a: $(ANIM_UTIL_OBJS)
|
||||||
examples/libexample_util.a: $(EX_UTIL_OBJS)
|
examples/libexample_util.a: $(EX_UTIL_OBJS)
|
||||||
examples/libgif2webp_util.a: $(GIF2WEBP_UTIL_OBJS)
|
examples/libgifdec.a: $(GIFDEC_OBJS)
|
||||||
|
extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS)
|
||||||
|
imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS)
|
||||||
|
imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS)
|
||||||
|
imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS)
|
||||||
src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
|
src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
|
||||||
src/libwebp.a: $(LIBWEBP_OBJS)
|
src/libwebp.a: $(LIBWEBP_OBJS)
|
||||||
src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
|
src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
|
||||||
@@ -271,28 +373,54 @@ src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS)
|
|||||||
%.a:
|
%.a:
|
||||||
$(AR) $(ARFLAGS) $@ $^
|
$(AR) $(ARFLAGS) $@ $^
|
||||||
|
|
||||||
examples/cwebp: examples/cwebp.o $(EX_FORMAT_DEC_OBJS)
|
examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS)
|
||||||
|
examples/cwebp: examples/cwebp.o
|
||||||
examples/dwebp: examples/dwebp.o
|
examples/dwebp: examples/dwebp.o
|
||||||
examples/gif2webp: examples/gif2webp.o
|
examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS)
|
||||||
examples/vwebp: examples/vwebp.o
|
examples/vwebp: examples/vwebp.o
|
||||||
examples/webpmux: examples/webpmux.o
|
examples/webpmux: examples/webpmux.o
|
||||||
|
examples/img2webp: examples/img2webp.o
|
||||||
|
|
||||||
examples/cwebp: examples/libexample_util.a src/libwebp.a
|
examples/anim_diff: examples/libanim_util.a examples/libgifdec.a
|
||||||
|
examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a
|
||||||
|
examples/anim_diff: imageio/libimageio_util.a src/libwebp.a
|
||||||
|
examples/anim_diff: EXTRA_LIBS += $(GIF_LIBS)
|
||||||
|
examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF
|
||||||
|
examples/cwebp: examples/libexample_util.a
|
||||||
|
examples/cwebp: imageio/libimagedec.a
|
||||||
|
examples/cwebp: imageio/libimageio_util.a
|
||||||
|
examples/cwebp: src/libwebp.a
|
||||||
examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS)
|
examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS)
|
||||||
examples/dwebp: examples/libexample_util.a src/libwebpdecoder.a
|
examples/dwebp: examples/libexample_util.a
|
||||||
|
examples/dwebp: imageio/libimagedec.a
|
||||||
|
examples/dwebp: imageio/libimageenc.a
|
||||||
|
examples/dwebp: imageio/libimageio_util.a
|
||||||
|
examples/dwebp: src/libwebp.a
|
||||||
examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS)
|
examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS)
|
||||||
examples/gif2webp: examples/libexample_util.a examples/libgif2webp_util.a
|
examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a
|
||||||
examples/gif2webp: src/mux/libwebpmux.a src/libwebp.a
|
examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a
|
||||||
examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS)
|
examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS)
|
||||||
examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
|
examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
|
||||||
examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
|
examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
|
||||||
examples/vwebp: src/libwebp.a
|
examples/vwebp: imageio/libimageio_util.a src/libwebp.a
|
||||||
examples/vwebp: EXTRA_LIBS += $(GL_LIBS)
|
examples/vwebp: EXTRA_LIBS += $(GL_LIBS)
|
||||||
examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL
|
examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL
|
||||||
examples/webpmux: examples/libexample_util.a src/mux/libwebpmux.a
|
examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a
|
||||||
examples/webpmux: src/libwebpdecoder.a
|
examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a
|
||||||
|
examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a
|
||||||
|
examples/img2webp: imageio/libimagedec.a
|
||||||
|
examples/img2webp: src/mux/libwebpmux.a src/libwebp.a
|
||||||
|
examples/img2webp: EXTRA_LIBS += $(CWEBP_LIBS)
|
||||||
|
|
||||||
$(OUT_EXAMPLES) $(EXTRA_EXAMPLES):
|
extras/get_disto: extras/get_disto.o
|
||||||
|
extras/get_disto: imageio/libimagedec.a imageio/libimageio_util.a src/libwebp.a
|
||||||
|
extras/get_disto: EXTRA_LIBS += $(CWEBP_LIBS)
|
||||||
|
|
||||||
|
extras/webp_quality: extras/webp_quality.o
|
||||||
|
extras/webp_quality: imageio/libimageio_util.a
|
||||||
|
extras/webp_quality: $(EXTRA_LIB) src/libwebp.a
|
||||||
|
|
||||||
|
$(OUT_EXAMPLES) $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES):
|
||||||
$(CC) -o $@ $^ $(LDFLAGS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
dist: DESTDIR := dist
|
dist: DESTDIR := dist
|
||||||
@@ -306,7 +434,8 @@ dist: all
|
|||||||
$(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
|
$(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
|
||||||
$(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
|
$(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
|
||||||
umask 022; \
|
umask 022; \
|
||||||
for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1; do \
|
for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \
|
||||||
|
man/img2webp.1; do \
|
||||||
basenam=$$(basename $$m .1); \
|
basenam=$$(basename $$m .1); \
|
||||||
$(GROFF) -t -e -man -T utf8 $$m \
|
$(GROFF) -t -e -man -T utf8 $$m \
|
||||||
| $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
|
| $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
|
||||||
@@ -317,6 +446,8 @@ dist: all
|
|||||||
clean:
|
clean:
|
||||||
$(RM) $(OUTPUT) *~ \
|
$(RM) $(OUTPUT) *~ \
|
||||||
examples/*.o examples/*~ \
|
examples/*.o examples/*~ \
|
||||||
|
extras/*.o extras/*~ \
|
||||||
|
imageio/*.o imageio/*~ \
|
||||||
src/dec/*.o src/dec/*~ \
|
src/dec/*.o src/dec/*~ \
|
||||||
src/demux/*.o src/demux/*~ \
|
src/demux/*.o src/demux/*~ \
|
||||||
src/dsp/*.o src/dsp/*~ \
|
src/dsp/*.o src/dsp/*~ \
|
||||||
@@ -325,19 +456,5 @@ clean:
|
|||||||
src/utils/*.o src/utils/*~ \
|
src/utils/*.o src/utils/*~ \
|
||||||
src/webp/*~ man/*~ doc/*~ swig/*~ \
|
src/webp/*~ man/*~ doc/*~ swig/*~ \
|
||||||
|
|
||||||
superclean: clean
|
.PHONY: all clean dist ex
|
||||||
$(RM) -r .git *.log *.cache *~
|
|
||||||
$(RM) -r .deps */.deps */*/.deps
|
|
||||||
$(RM) -r .libs */.libs */*/.libs
|
|
||||||
$(RM) */*.lo */*/*.lo
|
|
||||||
$(RM) */*.la */*/*.la
|
|
||||||
$(RM) Makefile */Makefile */*/Makefile
|
|
||||||
$(RM) Makefile.in */Makefile.in */*/Makefile.in
|
|
||||||
$(RM) config.log autom4te.cache libtool config.h stamp-h1
|
|
||||||
$(RM) aclocal.m4 compile
|
|
||||||
$(RM) config.guess config.h.in config.sub config.status
|
|
||||||
$(RM) configure depcomp install-sh ltmain.sh missing src/libwebp.pc
|
|
||||||
$(RM) m4/*
|
|
||||||
|
|
||||||
.PHONY: all clean dist ex superclean
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
|
|||||||
298
man/cwebp.1
298
man/cwebp.1
@@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH CWEBP 1 "July 22, 2014"
|
.TH CWEBP 1 "January 20, 2017"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
cwebp \- compress an image file to a WebP file
|
cwebp \- compress an image file to a WebP file
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -23,7 +23,7 @@ Using "\-" as output name will direct output to 'stdout'.
|
|||||||
.TP
|
.TP
|
||||||
.BI \-\- " string
|
.BI \-\- " string
|
||||||
Explicitly specify the input file. This option is useful if the input
|
Explicitly specify the input file. This option is useful if the input
|
||||||
file starts with an '\-' for instance. This option must appear \fBlast\fP.
|
file starts with a '\-' for instance. This option must appear \fBlast\fP.
|
||||||
Any other options afterward will be ignored.
|
Any other options afterward will be ignored.
|
||||||
.TP
|
.TP
|
||||||
.B \-h, \-help
|
.B \-h, \-help
|
||||||
@@ -35,53 +35,49 @@ A summary of all the possible options.
|
|||||||
.B \-version
|
.B \-version
|
||||||
Print the version number (as major.minor.revision) and exit.
|
Print the version number (as major.minor.revision) and exit.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-lossless
|
||||||
|
Encode the image without any loss. For images with fully transparent area,
|
||||||
|
the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the
|
||||||
|
\-exact option is used.
|
||||||
|
.TP
|
||||||
|
.BI \-near_lossless " int
|
||||||
|
Use near\-lossless image preprocessing. This option adjusts pixel values
|
||||||
|
to help compressibility, but has minimal impact on the visual quality.
|
||||||
|
It triggers lossless compression mode automatically.
|
||||||
|
Range is 0 (maximum preprocessing) to 100 (no preprocessing, the default).
|
||||||
|
.TP
|
||||||
.BI \-q " float
|
.BI \-q " float
|
||||||
Specify the compression factor for RGB channels between 0 and 100. The default
|
Specify the compression factor for RGB channels between 0 and 100. The default
|
||||||
is 75.
|
is 75.
|
||||||
.\" TODO(jzern): restore post-v0.4.1
|
.br
|
||||||
.\" .TP
|
In case of lossy compression (default), a small factor produces a smaller file
|
||||||
.\" .BI \-z " int
|
with lower quality. Best quality is achieved by using a value of 100.
|
||||||
.\" Switch on \fBlossless\fP compression mode with the specified level between 0
|
.br
|
||||||
.\" and 9, with level 0 being the fastest, 9 being the slowest. Fast mode
|
In case of lossless compression (specified by the \fB\-lossless\fP option), a
|
||||||
.\" produces larger file size than slower ones. A good default is \-z 6.
|
small factor enables faster compression speed, but produces a larger file.
|
||||||
.\" This option is actually a shortcut for some predefined settings for quality
|
Maximum compression is achieved by using a value of 100.
|
||||||
.\" and method. If options \-q or \-m are subsequently used, they will invalidate
|
.TP
|
||||||
.\" the effect of this \-z option.
|
.BI \-z " int
|
||||||
.\" .br
|
Switch on \fBlossless\fP compression mode with the specified level between 0
|
||||||
.\" In case of lossy compression (default), a small factor produces a smaller file
|
and 9, with level 0 being the fastest, 9 being the slowest. Fast mode
|
||||||
.\" with lower quality. Best quality is achieved by using a value of 100.
|
produces larger file size than slower ones. A good default is \fB\-z 6\fP.
|
||||||
.\" .br
|
This option is actually a shortcut for some predefined settings for quality
|
||||||
.\" In case of lossless compression (specified by the \-lossless option), a small
|
and method. If options \fB\-q\fP or \fB\-m\fP are subsequently used, they will
|
||||||
.\" factor enables faster compression speed, but produces a larger file. Maximum
|
invalidate the effect of this option.
|
||||||
.\" compression is achieved by using a value of 100.
|
|
||||||
.TP
|
.TP
|
||||||
.BI \-alpha_q " int
|
.BI \-alpha_q " int
|
||||||
Specify the compression factor for alpha compression between 0 and 100.
|
Specify the compression factor for alpha compression between 0 and 100.
|
||||||
Lossless compression of alpha is achieved using a value of 100, while the lower
|
Lossless compression of alpha is achieved using a value of 100, while the lower
|
||||||
values result in a lossy compression. The default is 100.
|
values result in a lossy compression. The default is 100.
|
||||||
.TP
|
.TP
|
||||||
.BI \-f " int
|
|
||||||
Specify the strength of the deblocking filter, between 0 (no filtering)
|
|
||||||
and 100 (maximum filtering). A value of 0 will turn off any filtering.
|
|
||||||
Higher value will increase the strength of the filtering process applied
|
|
||||||
after decoding the picture. The higher the value the smoother the picture will
|
|
||||||
appear. Typical values are usually in the range of 20 to 50.
|
|
||||||
.TP
|
|
||||||
.BI \-preset " string
|
.BI \-preset " string
|
||||||
Specify a set of pre-defined parameters to suit a particular type of
|
Specify a set of pre\-defined parameters to suit a particular type of
|
||||||
source material. Possible values are: \fBdefault\fP, \fBphoto\fP,
|
source material. Possible values are: \fBdefault\fP, \fBphoto\fP,
|
||||||
\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since
|
\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since
|
||||||
\fB\-preset\fP overwrites the other parameters' values (except the
|
\fB\-preset\fP overwrites the other parameters' values (except the
|
||||||
\fB\-q\fP one), this option should preferably appear first in the
|
\fB\-q\fP one), this option should preferably appear first in the
|
||||||
order of the arguments.
|
order of the arguments.
|
||||||
.TP
|
.TP
|
||||||
.BI \-sns " int
|
|
||||||
Specify the amplitude of the spatial noise shaping. Spatial noise shaping
|
|
||||||
(or \fBsns\fP for short) refers to a general collection of built-in algorithms
|
|
||||||
used to decide which area of the picture should use relatively less bits,
|
|
||||||
and where else to better transfer these bits. The possible range goes from
|
|
||||||
0 (algorithm is off) to 100 (the maximal effect). The default value is 80.
|
|
||||||
.TP
|
|
||||||
.BI \-m " int
|
.BI \-m " int
|
||||||
Specify the compression method to use. This parameter controls the
|
Specify the compression method to use. This parameter controls the
|
||||||
trade off between encoding speed and the compressed file size and quality.
|
trade off between encoding speed and the compressed file size and quality.
|
||||||
@@ -91,14 +87,18 @@ additional encoding possibilities and decide on the quality gain.
|
|||||||
Lower value can result in faster processing time at the expense of
|
Lower value can result in faster processing time at the expense of
|
||||||
larger file size and lower compression quality.
|
larger file size and lower compression quality.
|
||||||
.TP
|
.TP
|
||||||
.B \-jpeg_like
|
.BI \-resize " width height
|
||||||
Change the internal parameter mapping to better match the expected size
|
Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
|
||||||
of JPEG compression. This flag will generally produce an output file of
|
If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
|
||||||
similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
|
the value will be calculated preserving the aspect\-ratio.
|
||||||
with less visual distortion.
|
.TP
|
||||||
|
.BI \-crop " x_position y_position width height
|
||||||
|
Crop the source to a rectangle with top\-left corner at coordinates
|
||||||
|
(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
|
||||||
|
This cropping area must be fully contained within the source rectangle.
|
||||||
.TP
|
.TP
|
||||||
.B \-mt
|
.B \-mt
|
||||||
Use multi-threading for encoding, if possible. This option is only effective
|
Use multi\-threading for encoding, if possible. This option is only effective
|
||||||
when using lossy compression on a source with a transparency channel.
|
when using lossy compression on a source with a transparency channel.
|
||||||
.TP
|
.TP
|
||||||
.B \-low_memory
|
.B \-low_memory
|
||||||
@@ -109,13 +109,50 @@ different in size and distortion. This flag is only effective for methods
|
|||||||
some side effects on the bitstream: it forces certain bitstream features
|
some side effects on the bitstream: it forces certain bitstream features
|
||||||
like number of partitions (forced to 1). Note that a more detailed report
|
like number of partitions (forced to 1). Note that a more detailed report
|
||||||
of bitstream size is printed by \fBcwebp\fP when using this option.
|
of bitstream size is printed by \fBcwebp\fP when using this option.
|
||||||
|
|
||||||
|
.SS LOSSY OPTIONS
|
||||||
|
These options are only effective when doing lossy encoding (the default, with
|
||||||
|
or without alpha).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI \-size " int
|
||||||
|
Specify a target size (in bytes) to try and reach for the compressed output.
|
||||||
|
The compressor will make several passes of partial encoding in order to get as
|
||||||
|
close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
|
||||||
|
are used, \fB\-size\fP value will prevail.
|
||||||
|
.TP
|
||||||
|
.BI \-psnr " float
|
||||||
|
Specify a target PSNR (in dB) to try and reach for the compressed output.
|
||||||
|
The compressor will make several passes of partial encoding in order to get as
|
||||||
|
close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
|
||||||
|
are used, \fB\-size\fP value will prevail.
|
||||||
|
.TP
|
||||||
|
.BI \-pass " int
|
||||||
|
Set a maximum number of passes to use during the dichotomy used by
|
||||||
|
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
|
||||||
|
If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
|
||||||
|
specified, a default value of '6' passes will be used.
|
||||||
.TP
|
.TP
|
||||||
.B \-af
|
.B \-af
|
||||||
Turns auto-filter on. This algorithm will spend additional time optimizing
|
Turns auto\-filter on. This algorithm will spend additional time optimizing
|
||||||
the filtering strength to reach a well-balanced quality.
|
the filtering strength to reach a well\-balanced quality.
|
||||||
|
.TP
|
||||||
|
.B \-jpeg_like
|
||||||
|
Change the internal parameter mapping to better match the expected size
|
||||||
|
of JPEG compression. This flag will generally produce an output file of
|
||||||
|
similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
|
||||||
|
with less visual distortion.
|
||||||
|
|
||||||
.SH ADDITIONAL OPTIONS
|
.TP
|
||||||
More advanced options are:
|
Advanced options:
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI \-f " int
|
||||||
|
Specify the strength of the deblocking filter, between 0 (no filtering)
|
||||||
|
and 100 (maximum filtering). A value of 0 will turn off any filtering.
|
||||||
|
Higher value will increase the strength of the filtering process applied
|
||||||
|
after decoding the picture. The higher the value the smoother the picture will
|
||||||
|
appear. Typical values are usually in the range of 20 to 50.
|
||||||
.TP
|
.TP
|
||||||
.BI \-sharpness " int
|
.BI \-sharpness " int
|
||||||
Specify the sharpness of the filtering (if used).
|
Specify the sharpness of the filtering (if used).
|
||||||
@@ -129,6 +166,17 @@ Use strong filtering (if filtering is being used thanks to the
|
|||||||
Disable strong filtering (if filtering is being used thanks to the
|
Disable strong filtering (if filtering is being used thanks to the
|
||||||
\fB\-f\fP option) and use simple filtering instead.
|
\fB\-f\fP option) and use simple filtering instead.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-sharp_yuv
|
||||||
|
Use more accurate and sharper RGB->YUV conversion if needed. Note that this
|
||||||
|
process is slower than the default 'fast' RGB->YUV conversion.
|
||||||
|
.TP
|
||||||
|
.BI \-sns " int
|
||||||
|
Specify the amplitude of the spatial noise shaping. Spatial noise shaping
|
||||||
|
(or \fBsns\fP for short) refers to a general collection of built\-in algorithms
|
||||||
|
used to decide which area of the picture should use relatively less bits,
|
||||||
|
and where else to better transfer these bits. The possible range goes from
|
||||||
|
0 (algorithm is off) to 100 (the maximal effect). The default value is 50.
|
||||||
|
.TP
|
||||||
.BI \-segments " int
|
.BI \-segments " int
|
||||||
Change the number of partitions to use during the segmentation of the
|
Change the number of partitions to use during the segmentation of the
|
||||||
sns algorithm. Segments should be in range 1 to 4. Default value is 4.
|
sns algorithm. Segments should be in range 1 to 4. Default value is 4.
|
||||||
@@ -138,107 +186,30 @@ is used.
|
|||||||
.BI \-partition_limit " int
|
.BI \-partition_limit " int
|
||||||
Degrade quality by limiting the number of bits used by some macroblocks.
|
Degrade quality by limiting the number of bits used by some macroblocks.
|
||||||
Range is 0 (no degradation, the default) to 100 (full degradation).
|
Range is 0 (no degradation, the default) to 100 (full degradation).
|
||||||
Useful values are usually around 30-70 for moderately large images.
|
Useful values are usually around 30\-70 for moderately large images.
|
||||||
In the VP8 format, the so-called control partition has a limit of 512k and
|
In the VP8 format, the so\-called control partition has a limit of 512k and
|
||||||
is used to store the following information: whether the macroblock is skipped,
|
is used to store the following information: whether the macroblock is skipped,
|
||||||
which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
|
which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
|
||||||
mode, and finally the prediction modes to use for each of the sub-blocks.
|
mode, and finally the prediction modes to use for each of the sub\-blocks.
|
||||||
For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
|
For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
|
||||||
The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
|
The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
|
||||||
information can use up almost all these 4 bits (although the case is unlikely),
|
information can use up almost all these 4 bits (although the case is unlikely),
|
||||||
which is problematic for very large images. The partition_limit factor controls
|
which is problematic for very large images. The partition_limit factor controls
|
||||||
how frequently the most bit-costly mode (intra 4x4) will be used. This is
|
how frequently the most bit\-costly mode (intra 4x4) will be used. This is
|
||||||
useful in case the 512k limit is reached and the following message is displayed:
|
useful in case the 512k limit is reached and the following message is displayed:
|
||||||
\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
|
\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
|
||||||
If using \fB-partition_limit\fP is not enough to meet the 512k constraint, one
|
If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one
|
||||||
should use less segments in order to save more header bits per macroblock.
|
should use less segments in order to save more header bits per macroblock.
|
||||||
See the \fB-segments\fP option.
|
See the \fB\-segments\fP option.
|
||||||
.TP
|
|
||||||
.BI \-size " int
|
|
||||||
Specify a target size (in bytes) to try and reach for the compressed output.
|
|
||||||
Compressor will make several pass of partial encoding in order to get as
|
|
||||||
close as possible to this target.
|
|
||||||
.TP
|
|
||||||
.BI \-psnr " float
|
|
||||||
Specify a target PSNR (in dB) to try and reach for the compressed output.
|
|
||||||
Compressor will make several pass of partial encoding in order to get as
|
|
||||||
close as possible to this target.
|
|
||||||
.TP
|
|
||||||
.BI \-pass " int
|
|
||||||
Set a maximum number of passes to use during the dichotomy used by
|
|
||||||
options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10.
|
|
||||||
.TP
|
|
||||||
.BI \-resize " width height
|
|
||||||
Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
|
|
||||||
If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
|
|
||||||
the value will be calculated preserving the aspect-ratio.
|
|
||||||
.TP
|
|
||||||
.BI \-crop " x_position y_position width height
|
|
||||||
Crop the source to a rectangle with top-left corner at coordinates
|
|
||||||
(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
|
|
||||||
This cropping area must be fully contained within the source rectangle.
|
|
||||||
.TP
|
|
||||||
.BI \-s " width height
|
|
||||||
Specify that the input file actually consists of raw Y'CbCr samples following
|
|
||||||
the ITU-R BT.601 recommendation, in 4:2:0 linear format.
|
|
||||||
The luma plane has size \fBwidth\fP x \fBheight\fP.
|
|
||||||
.TP
|
|
||||||
.BI \-map " int
|
|
||||||
Output additional ASCII-map of encoding information. Possible map values
|
|
||||||
range from 1 to 6. This is only meant to help debugging.
|
|
||||||
.TP
|
|
||||||
.BI \-pre " int
|
|
||||||
Specify some pre-processing steps. Using a value of '2' will trigger
|
|
||||||
quality-dependent pseudo-random dithering during RGBA->YUVA conversion
|
|
||||||
(lossy compression only).
|
|
||||||
.TP
|
|
||||||
.BI \-alpha_filter " string
|
|
||||||
Specify the predictive filtering method for the alpha plane. One of 'none',
|
|
||||||
\&'fast' or 'best', in increasing complexity and slowness order. Default is
|
|
||||||
\&'fast'. Internally, alpha filtering is performed using four possible
|
|
||||||
predictions (none, horizontal, vertical, gradient). The 'best' mode will try
|
|
||||||
each mode in turn and pick the one which gives the smaller size. The 'fast'
|
|
||||||
mode will just try to form an a-priori guess without testing all modes.
|
|
||||||
.TP
|
|
||||||
.BI \-alpha_method " int
|
|
||||||
Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
|
|
||||||
no compression, 1 uses WebP lossless format for compression. The default is 1.
|
|
||||||
.TP
|
|
||||||
.B \-alpha_cleanup
|
|
||||||
Modify unseen RGB values under fully transparent area, to help compressibility.
|
|
||||||
The default is off.
|
|
||||||
.TP
|
|
||||||
.BI \-blend_alpha " int
|
|
||||||
This option blends the alpha channel (if present) with the source using the
|
|
||||||
background color specified in hexadecimal as 0xrrggbb. The alpha channel is
|
|
||||||
afterward reset to the opaque value 255.
|
|
||||||
.TP
|
|
||||||
.B \-noalpha
|
|
||||||
Using this option will discard the alpha channel.
|
|
||||||
.TP
|
|
||||||
.B \-lossless
|
|
||||||
Encode the image without any loss.
|
|
||||||
.TP
|
|
||||||
.BI \-hint " string
|
|
||||||
Specify the hint about input image type. Possible values are:
|
|
||||||
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
|
|
||||||
.TP
|
|
||||||
.BI \-metadata " string
|
|
||||||
A comma separated list of metadata to copy from the input to the output if
|
|
||||||
present.
|
|
||||||
Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
|
|
||||||
The default is \fBnone\fP.
|
|
||||||
|
|
||||||
Note: each input format may not support all combinations.
|
.SS LOGGING OPTIONS
|
||||||
.TP
|
These options control the level of output:
|
||||||
.B \-noasm
|
|
||||||
Disable all assembly optimizations.
|
|
||||||
.TP
|
.TP
|
||||||
.B \-v
|
.B \-v
|
||||||
Print extra information (encoding time in particular).
|
Print extra information (encoding time in particular).
|
||||||
.TP
|
.TP
|
||||||
.B \-print_psnr
|
.B \-print_psnr
|
||||||
Compute and report average PSNR (Peak-Signal-To-Noise ratio).
|
Compute and report average PSNR (Peak\-Signal\-To\-Noise ratio).
|
||||||
.TP
|
.TP
|
||||||
.B \-print_ssim
|
.B \-print_ssim
|
||||||
Compute and report average SSIM (structural similarity
|
Compute and report average SSIM (structural similarity
|
||||||
@@ -255,14 +226,70 @@ Report encoding progress in percent.
|
|||||||
Do not print anything.
|
Do not print anything.
|
||||||
.TP
|
.TP
|
||||||
.B \-short
|
.B \-short
|
||||||
Only print brief information (output file size and PSNR) for testing purpose.
|
Only print brief information (output file size and PSNR) for testing purposes.
|
||||||
|
.TP
|
||||||
|
.BI \-map " int
|
||||||
|
Output additional ASCII\-map of encoding information. Possible map values
|
||||||
|
range from 1 to 6. This is only meant to help debugging.
|
||||||
|
|
||||||
|
.SS ADDITIONAL OPTIONS
|
||||||
|
More advanced options are:
|
||||||
|
.TP
|
||||||
|
.BI \-s " width height
|
||||||
|
Specify that the input file actually consists of raw Y'CbCr samples following
|
||||||
|
the ITU\-R BT.601 recommendation, in 4:2:0 linear format.
|
||||||
|
The luma plane has size \fBwidth\fP x \fBheight\fP.
|
||||||
|
.TP
|
||||||
|
.BI \-pre " int
|
||||||
|
Specify some preprocessing steps. Using a value of '2' will trigger
|
||||||
|
quality\-dependent pseudo\-random dithering during RGBA\->YUVA conversion
|
||||||
|
(lossy compression only).
|
||||||
|
.TP
|
||||||
|
.BI \-alpha_filter " string
|
||||||
|
Specify the predictive filtering method for the alpha plane. One of 'none',
|
||||||
|
\&'fast' or 'best', in increasing complexity and slowness order. Default is
|
||||||
|
\&'fast'. Internally, alpha filtering is performed using four possible
|
||||||
|
predictions (none, horizontal, vertical, gradient). The 'best' mode will try
|
||||||
|
each mode in turn and pick the one which gives the smaller size. The 'fast'
|
||||||
|
mode will just try to form an a priori guess without testing all modes.
|
||||||
|
.TP
|
||||||
|
.BI \-alpha_method " int
|
||||||
|
Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
|
||||||
|
no compression, 1 uses WebP lossless format for compression. The default is 1.
|
||||||
|
.TP
|
||||||
|
.B \-exact
|
||||||
|
Preserve RGB values in transparent area. The default is off, to help
|
||||||
|
compressibility.
|
||||||
|
.TP
|
||||||
|
.BI \-blend_alpha " int
|
||||||
|
This option blends the alpha channel (if present) with the source using the
|
||||||
|
background color specified in hexadecimal as 0xrrggbb. The alpha channel is
|
||||||
|
afterward reset to the opaque value 255.
|
||||||
|
.TP
|
||||||
|
.B \-noalpha
|
||||||
|
Using this option will discard the alpha channel.
|
||||||
|
.TP
|
||||||
|
.BI \-hint " string
|
||||||
|
Specify the hint about input image type. Possible values are:
|
||||||
|
\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
|
||||||
|
.TP
|
||||||
|
.BI \-metadata " string
|
||||||
|
A comma separated list of metadata to copy from the input to the output if
|
||||||
|
present.
|
||||||
|
Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
|
||||||
|
The default is \fBnone\fP.
|
||||||
|
|
||||||
|
Note: each input format may not support all combinations.
|
||||||
|
.TP
|
||||||
|
.B \-noasm
|
||||||
|
Disable all assembly optimizations.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
.br
|
.br
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting\-patches/
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
|
cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
|
||||||
@@ -274,9 +301,10 @@ cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp
|
|||||||
cwebp \-o picture.webp \-\- \-\-\-picture.png
|
cwebp \-o picture.webp \-\- \-\-\-picture.png
|
||||||
|
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
\fBcwebp\fP was written by the WebP team.
|
\fBcwebp\fP is a part of libwebp and was written by the WebP team.
|
||||||
.br
|
.br
|
||||||
The latest source tree is available at http://www.webmproject.org/code
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
.PP
|
.PP
|
||||||
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
|
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
|
||||||
for the Debian project (and may be used by others).
|
for the Debian project (and may be used by others).
|
||||||
|
|||||||
36
man/dwebp.1
36
man/dwebp.1
@@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH DWEBP 1 "July 22, 2014"
|
.TH DWEBP 1 "June 23, 2016"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dwebp \- decompress a WebP file to an image file
|
dwebp \- decompress a WebP file to an image file
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -67,12 +67,11 @@ but it will make the decoding faster.
|
|||||||
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
|
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
|
||||||
post-processing effect applied to chroma components in lossy compression.
|
post-processing effect applied to chroma components in lossy compression.
|
||||||
It helps by smoothing gradients and avoiding banding artifacts.
|
It helps by smoothing gradients and avoiding banding artifacts.
|
||||||
.\" TODO(jzern): restore post-v0.4.1
|
.TP
|
||||||
.\" .TP
|
.BI \-alpha_dither
|
||||||
.\" .BI \-alpha_dither
|
If the compressed file contains a transparency plane that was quantized
|
||||||
.\" If the compressed file contains a transparency plane that was quantized
|
during compression, this flag will allow dithering the reconstructed plane
|
||||||
.\" during compression, this flag will allow dithering the reconstructed plane
|
in order to generate smoother transparency gradients.
|
||||||
.\" in order to generate smoother transparency gradients.
|
|
||||||
.TP
|
.TP
|
||||||
.B \-nodither
|
.B \-nodither
|
||||||
Disable all dithering (default).
|
Disable all dithering (default).
|
||||||
@@ -87,16 +86,20 @@ This cropping area must be fully contained within the source rectangle.
|
|||||||
The top-left corner will be snapped to even coordinates if needed.
|
The top-left corner will be snapped to even coordinates if needed.
|
||||||
This option is meant to reduce the memory needed for cropping large images.
|
This option is meant to reduce the memory needed for cropping large images.
|
||||||
Note: the cropping is applied \fIbefore\fP any scaling.
|
Note: the cropping is applied \fIbefore\fP any scaling.
|
||||||
.\" TODO(jzern): restore post-v0.4.1
|
|
||||||
.\" .TP
|
|
||||||
.\" .B \-flip
|
|
||||||
.\" Flip decoded image vertically (can be useful for OpenGL textures for instance).
|
|
||||||
.TP
|
.TP
|
||||||
.BI \-scale " width height
|
.B \-flip
|
||||||
|
Flip decoded image vertically (can be useful for OpenGL textures for instance).
|
||||||
|
.TP
|
||||||
|
\fB\-resize\fR, \fB\-scale\fI width height\fR
|
||||||
Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
|
Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
|
||||||
option is mostly intended to reducing the memory needed to decode large images,
|
option is mostly intended to reducing the memory needed to decode large images,
|
||||||
when only a small version is needed (thumbnail, preview, etc.). Note: scaling
|
when only a small version is needed (thumbnail, preview, etc.). Note: scaling
|
||||||
is applied \fIafter\fP cropping.
|
is applied \fIafter\fP cropping.
|
||||||
|
If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
|
||||||
|
the value will be calculated preserving the aspect-ratio.
|
||||||
|
.TP
|
||||||
|
.B \-quiet
|
||||||
|
Do not print anything.
|
||||||
.TP
|
.TP
|
||||||
.B \-v
|
.B \-v
|
||||||
Print extra information (decoding time in particular).
|
Print extra information (decoding time in particular).
|
||||||
@@ -105,8 +108,8 @@ Print extra information (decoding time in particular).
|
|||||||
Disable all assembly optimizations.
|
Disable all assembly optimizations.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
.br
|
.br
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting-patches/
|
||||||
@@ -121,9 +124,10 @@ dwebp \-o output.ppm \-\- \-\-\-picture.webp
|
|||||||
cat picture.webp | dwebp \-o \- \-\- \- > output.ppm
|
cat picture.webp | dwebp \-o \- \-\- \- > output.ppm
|
||||||
|
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
\fBdwebp\fP was written by the WebP team.
|
\fBdwebp\fP is a part of libwebp and was written by the WebP team.
|
||||||
.br
|
.br
|
||||||
The latest source tree is available at http://www.webmproject.org/code
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
.PP
|
.PP
|
||||||
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
|
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
|
||||||
for the Debian project (and may be used by others).
|
for the Debian project (and may be used by others).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH GIF2WEBP 1 "March 7, 2014"
|
.TH GIF2WEBP 1 "January 25, 2017"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
gif2webp \- Convert a GIF image to WebP
|
gif2webp \- Convert a GIF image to WebP
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -54,6 +54,12 @@ additional encoding possibilities and decide on the quality gain.
|
|||||||
Lower value can result is faster processing time at the expense of
|
Lower value can result is faster processing time at the expense of
|
||||||
larger file size and lower compression quality.
|
larger file size and lower compression quality.
|
||||||
.TP
|
.TP
|
||||||
|
.BI \-min_size
|
||||||
|
Encode image to achieve smallest size. This disables key frame insertion and
|
||||||
|
picks the dispose method resulting in smallest output for each frame. It uses
|
||||||
|
lossless compression by default, but can be combined with \-q, \-m, \-lossy or
|
||||||
|
\-mixed options.
|
||||||
|
.TP
|
||||||
.BI \-kmin " int
|
.BI \-kmin " int
|
||||||
.TP
|
.TP
|
||||||
.BI \-kmax " int
|
.BI \-kmax " int
|
||||||
@@ -62,7 +68,9 @@ Specify the minimum and maximum distance between consecutive key frames
|
|||||||
some key frames into the output animation as needed so that this criteria is
|
some key frames into the output animation as needed so that this criteria is
|
||||||
satisfied.
|
satisfied.
|
||||||
.br
|
.br
|
||||||
A 'kmin' value of 0 will turn off insertion of key frames.
|
A 'kmax' value of 0 will turn off insertion of key frames. A 'kmax' value of 1
|
||||||
|
will result in all frames being key frames. 'kmin' value is not taken into
|
||||||
|
account in both these special cases.
|
||||||
Typical values are in the range 3 to 30. Default values are kmin = 9,
|
Typical values are in the range 3 to 30. Default values are kmin = 9,
|
||||||
kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression.
|
kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression.
|
||||||
.br
|
.br
|
||||||
@@ -110,8 +118,8 @@ Print extra information.
|
|||||||
Do not print anything.
|
Do not print anything.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
.br
|
.br
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting-patches/
|
||||||
@@ -128,9 +136,10 @@ gif2webp \-lossy \-f 50 picture.gif \-o picture.webp
|
|||||||
gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif
|
gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif
|
||||||
|
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
\fBgif2webp\fP was written by the WebP team.
|
\fBgif2webp\fP is a part of libwebp and was written by the WebP team.
|
||||||
.br
|
.br
|
||||||
The latest source tree is available at http://www.webmproject.org/code
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
.PP
|
.PP
|
||||||
This manual page was written by Urvang Joshi <urvang@google.com>, for the
|
This manual page was written by Urvang Joshi <urvang@google.com>, for the
|
||||||
Debian project (and may be used by others).
|
Debian project (and may be used by others).
|
||||||
|
|||||||
97
man/img2webp.1
Normal file
97
man/img2webp.1
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
|
.TH IMG2WEBP 1 "January 23, 2017"
|
||||||
|
.SH NAME
|
||||||
|
img2webp \- create animated WebP file from a sequence of input images.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B img2webp
|
||||||
|
[file_level_options] [files] [per_frame_options...]
|
||||||
|
.br
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This manual page documents the
|
||||||
|
.B img2webp
|
||||||
|
command.
|
||||||
|
.PP
|
||||||
|
\fBimg2webp\fP compresses a sequence of images using the animated WebP format.
|
||||||
|
Input images can either be PNG, JPEG, TIFF or WebP.
|
||||||
|
.SH FILE-LEVEL OPTIONS
|
||||||
|
The file-level options are applied at the beginning of the compression process,
|
||||||
|
before the input frames are read.
|
||||||
|
.TP
|
||||||
|
.BI \-o " string
|
||||||
|
Specify the name of the output WebP file.
|
||||||
|
.TP
|
||||||
|
.BI \-min_size
|
||||||
|
Encode images to achieve smallest size. This disables key frame insertion and
|
||||||
|
picks the parameters resulting in smallest output for each frame. It uses
|
||||||
|
lossless compression by default, but can be combined with \-q, \-m, \-lossy or
|
||||||
|
\-mixed options.
|
||||||
|
.TP
|
||||||
|
.BI \-kmin " int
|
||||||
|
.TP
|
||||||
|
.BI \-kmax " int
|
||||||
|
Specify the minimum and maximum distance between consecutive key frames
|
||||||
|
(independently decodable frames) in the output animation. The tool will insert
|
||||||
|
some key frames into the output animation as needed so that this criteria is
|
||||||
|
satisfied.
|
||||||
|
.br
|
||||||
|
.B \-mixed
|
||||||
|
Mixed compression mode: optimize compression of the image by picking either
|
||||||
|
lossy or lossless compression for each frame heuristically. This global
|
||||||
|
option disables the local option \fB-lossy\fP and \fB-lossless\fP .
|
||||||
|
.TP
|
||||||
|
.BI \-loop " int
|
||||||
|
Specifies the number of times the animation should loop. Using '0' means
|
||||||
|
'loop indefinitely'.
|
||||||
|
.TP
|
||||||
|
.BI \-v
|
||||||
|
Be more verbose.
|
||||||
|
.TP
|
||||||
|
.B \-h, \-help
|
||||||
|
A short usage summary.
|
||||||
|
|
||||||
|
.SH PER-FRAME OPTIONS
|
||||||
|
The per-frame options are applied for the images following as arguments in the
|
||||||
|
command line. They can be modified any number of times preceding each particular
|
||||||
|
input image.
|
||||||
|
.TP
|
||||||
|
.BI \-d " int
|
||||||
|
Specify the image duration in milliseconds.
|
||||||
|
.TP
|
||||||
|
.B \-lossless, \-lossy
|
||||||
|
Compress the next image(s) using lossless or lossy compression mode. The
|
||||||
|
default mode is lossless.
|
||||||
|
.TP
|
||||||
|
.BI \-q " float
|
||||||
|
Specify the compression factor between 0 and 100. The default is 75.
|
||||||
|
.TP
|
||||||
|
.BI \-m " int
|
||||||
|
Specify the compression method to use. This parameter controls the
|
||||||
|
trade off between encoding speed and the compressed file size and quality.
|
||||||
|
Possible values range from 0 to 6. Default value is 4.
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp
|
||||||
|
.br
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
Please report all bugs to the issue tracker:
|
||||||
|
https://bugs.chromium.org/p/webp
|
||||||
|
.br
|
||||||
|
Patches welcome! See this page to get started:
|
||||||
|
http://www.webmproject.org/code/contribute/submitting\-patches/
|
||||||
|
|
||||||
|
.SH AUTHORS
|
||||||
|
\fBimg2webp\fP is a part of libwebp and was written by the WebP team.
|
||||||
|
.br
|
||||||
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
|
.PP
|
||||||
|
This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
|
||||||
|
for the Debian project (and may be used by others).
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR webpmux (1),
|
||||||
|
.BR gif2webp (1)
|
||||||
|
.br
|
||||||
|
Please refer to http://developers.google.com/speed/webp/ for additional
|
||||||
|
information.
|
||||||
23
man/vwebp.1
23
man/vwebp.1
@@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH VWEBP 1 "July 23, 2014"
|
.TH VWEBP 1 "November 25, 2016"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
vwebp \- decompress a WebP file and display it in a window
|
vwebp \- decompress a WebP file and display it in a window
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -33,11 +33,10 @@ Disable in-loop filtering.
|
|||||||
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
|
Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
|
||||||
post-processing effect applied to chroma components in lossy compression.
|
post-processing effect applied to chroma components in lossy compression.
|
||||||
It helps by smoothing gradients and avoiding banding artifacts. Default: 50.
|
It helps by smoothing gradients and avoiding banding artifacts. Default: 50.
|
||||||
.\" TODO(jzern): restore post-v0.4.1
|
.TP
|
||||||
.\" .TP
|
.BI \-noalphadither
|
||||||
.\" .BI \-noalphadither
|
By default, quantized transparency planes are dithered during decompression,
|
||||||
.\" By default, quantized transparency planes are dithered during decompression,
|
to smooth the gradients. This flag will prevent this dithering.
|
||||||
.\" to smooth the gradients. This flag will prevent this dithering.
|
|
||||||
.TP
|
.TP
|
||||||
.B \-mt
|
.B \-mt
|
||||||
Use multi-threading for decoding, if possible.
|
Use multi-threading for decoding, if possible.
|
||||||
@@ -60,12 +59,15 @@ Toggle use of color profile.
|
|||||||
.B 'i'
|
.B 'i'
|
||||||
Overlay file information.
|
Overlay file information.
|
||||||
.TP
|
.TP
|
||||||
|
.B 'd'
|
||||||
|
Disable blending and disposal process, for debugging purposes.
|
||||||
|
.TP
|
||||||
.B 'q' / 'Q' / ESC
|
.B 'q' / 'Q' / ESC
|
||||||
Quit.
|
Quit.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
.br
|
.br
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting-patches/
|
||||||
@@ -78,9 +80,10 @@ vwebp picture.webp -mt -dither 0
|
|||||||
vwebp \-\- \-\-\-picture.webp
|
vwebp \-\- \-\-\-picture.webp
|
||||||
|
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
\fBvwebp\fP was written by the WebP team.
|
\fBvwebp\fP is a part of libwebp and was written by the WebP team.
|
||||||
.br
|
.br
|
||||||
The latest source tree is available at http://www.webmproject.org/code
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
.PP
|
.PP
|
||||||
This manual page was written for the Debian project (and may be used by others).
|
This manual page was written for the Debian project (and may be used by others).
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH WEBPMUX 1 "December 17, 2013"
|
.TH WEBPMUX 1 "November 10, 2016"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
webpmux \- command line tool to create WebP Mux/container file.
|
webpmux \- create animated WebP files from non\-animated WebP images, extract
|
||||||
|
frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B webpmux \-get
|
.B webpmux \-get
|
||||||
.I GET_OPTIONS
|
.I GET_OPTIONS
|
||||||
@@ -34,6 +35,13 @@ webpmux \- command line tool to create WebP Mux/container file.
|
|||||||
.I OUTPUT
|
.I OUTPUT
|
||||||
.RE
|
.RE
|
||||||
.br
|
.br
|
||||||
|
.B webpmux \-duration
|
||||||
|
.I DURATION OPTIONS
|
||||||
|
.B [ \-duration ... ]
|
||||||
|
.I INPUT
|
||||||
|
.B \-o
|
||||||
|
.I OUTPUT
|
||||||
|
.br
|
||||||
.B webpmux \-info
|
.B webpmux \-info
|
||||||
.I INPUT
|
.I INPUT
|
||||||
.br
|
.br
|
||||||
@@ -45,8 +53,8 @@ This manual page documents the
|
|||||||
.B webpmux
|
.B webpmux
|
||||||
command.
|
command.
|
||||||
.PP
|
.PP
|
||||||
\fBwebpmux\fP can be used to create a WebP container file
|
\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as
|
||||||
and extract/strip relevant data from the container file.
|
to add/extract/strip XMP/EXIF metadata and ICC profile.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.SS GET_OPTIONS (\-get):
|
.SS GET_OPTIONS (\-get):
|
||||||
.TP
|
.TP
|
||||||
@@ -60,7 +68,7 @@ Get EXIF metadata.
|
|||||||
Get XMP metadata.
|
Get XMP metadata.
|
||||||
.TP
|
.TP
|
||||||
.BI frame " n
|
.BI frame " n
|
||||||
Get nth frame.
|
Get nth frame from an animated image. (n = 0 has a special meaning: last frame).
|
||||||
|
|
||||||
.SS SET_OPTIONS (\-set)
|
.SS SET_OPTIONS (\-set)
|
||||||
.TP
|
.TP
|
||||||
@@ -90,13 +98,50 @@ Strip EXIF metadata.
|
|||||||
.B xmp
|
.B xmp
|
||||||
Strip XMP metadata.
|
Strip XMP metadata.
|
||||||
|
|
||||||
|
.SS DURATION_OPTIONS (\-duration)
|
||||||
|
Amend the duration of a specific interval of frames. This option is only
|
||||||
|
effective on animated WebP and has no effect on a single-frame file.
|
||||||
|
.TP
|
||||||
|
.I duration[,start[,end]]
|
||||||
|
Where:
|
||||||
|
.br
|
||||||
|
.B duration
|
||||||
|
is the duration for the interval in milliseconds (mandatory).
|
||||||
|
Must be non-negative.
|
||||||
|
.br
|
||||||
|
.B start
|
||||||
|
is the starting frame index of the interval (optional).
|
||||||
|
.br
|
||||||
|
.B end
|
||||||
|
is the ending frame index (inclusive) of the interval (optional).
|
||||||
|
.TP
|
||||||
|
The three typical usages of this option are:
|
||||||
|
.br
|
||||||
|
.B -duration d
|
||||||
|
set the duration to 'd' for the whole animation.
|
||||||
|
.br
|
||||||
|
.B -duration d,f
|
||||||
|
set the duration of frame 'f' to 'd'.
|
||||||
|
.br
|
||||||
|
.B -duration d,start,end
|
||||||
|
set the duration to 'd' for the whole [start,end] interval.
|
||||||
|
.TP
|
||||||
|
.P
|
||||||
|
Note that the frames outside of the [start, end] interval will remain untouched.
|
||||||
|
The 'end' value '0' has the special meaning 'last frame of the animation'.
|
||||||
|
.TP
|
||||||
|
.I Reminder:
|
||||||
|
frame indexing starts at '1'.
|
||||||
|
.br
|
||||||
|
|
||||||
.SS FRAME_OPTIONS (\-frame)
|
.SS FRAME_OPTIONS (\-frame)
|
||||||
|
Create an animated WebP file from multiple (non\-animated) WebP images.
|
||||||
.TP
|
.TP
|
||||||
.I file_i +di[+xi+yi[+mi[bi]]]
|
.I file_i +di[+xi+yi[+mi[bi]]]
|
||||||
Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
|
Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
|
||||||
offset for this frame, 'di' is the pause duration before next frame, 'mi' is
|
offset for this frame, 'di' is the pause duration before next frame, 'mi' is
|
||||||
the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is
|
the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is
|
||||||
the blending method for this frame (+b for BLEND or -b for NO_BLEND).
|
the blending method for this frame (+b for BLEND or \-b for NO_BLEND).
|
||||||
Argument 'bi' can be omitted and will default to +b (BLEND).
|
Argument 'bi' can be omitted and will default to +b (BLEND).
|
||||||
Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE).
|
Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE).
|
||||||
Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will
|
Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will
|
||||||
@@ -126,41 +171,65 @@ Output file in WebP format.
|
|||||||
The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
|
The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Please report all bugs to our issue tracker:
|
Please report all bugs to the issue tracker:
|
||||||
http://code.google.com/p/webp/issues
|
https://bugs.chromium.org/p/webp
|
||||||
.br
|
.br
|
||||||
Patches welcome! See this page to get started:
|
Patches welcome! See this page to get started:
|
||||||
http://www.webmproject.org/code/contribute/submitting-patches/
|
http://www.webmproject.org/code/contribute/submitting\-patches/
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
|
.P
|
||||||
|
Add ICC profile:
|
||||||
|
.br
|
||||||
webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
|
webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
|
||||||
|
.P
|
||||||
|
Extract ICC profile:
|
||||||
.br
|
.br
|
||||||
webpmux \-get icc icc_container.webp \-o image_profile.icc
|
webpmux \-get icc icc_container.webp \-o image_profile.icc
|
||||||
|
.P
|
||||||
|
Strip ICC profile:
|
||||||
.br
|
.br
|
||||||
webpmux \-strip icc icc_container.webp \-o without_icc.webp
|
webpmux \-strip icc icc_container.webp \-o without_icc.webp
|
||||||
|
.P
|
||||||
|
Add XMP metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
|
webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
|
||||||
|
.P
|
||||||
|
Extract XMP metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
|
webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
|
||||||
|
.P
|
||||||
|
Strip XMP metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp
|
webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp
|
||||||
|
.P
|
||||||
|
Add EXIF metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp
|
webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp
|
||||||
|
.P
|
||||||
|
Extract EXIF metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-get exif exif_container.webp \-o image_metadata.exif
|
webpmux \-get exif exif_container.webp \-o image_metadata.exif
|
||||||
|
.P
|
||||||
|
Strip EXIF metadata:
|
||||||
.br
|
.br
|
||||||
webpmux \-strip exif exif_container.webp \-o without_exif.webp
|
webpmux \-strip exif exif_container.webp \-o without_exif.webp
|
||||||
|
.P
|
||||||
|
Create an animated WebP file from 3 (non\-animated) WebP images:
|
||||||
.br
|
.br
|
||||||
webpmux \-frame anim_1.webp +100 \-frame anim_2.webp +100+50+50
|
webpmux \-frame 1.webp +100 \-frame 2.webp +100+50+50
|
||||||
.br
|
.br
|
||||||
.RS 8
|
.RS 8
|
||||||
\-frame anim_2.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255
|
\-frame 3.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255
|
||||||
.br
|
.br
|
||||||
.RS 8
|
|
||||||
\-o anim_container.webp
|
\-o anim_container.webp
|
||||||
.RE
|
.RE
|
||||||
|
.P
|
||||||
|
Get the 2nd frame from an animated WebP file:
|
||||||
.br
|
.br
|
||||||
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
|
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
|
||||||
|
.P
|
||||||
|
Using \-get/\-set/\-strip with input file name starting with '\-':
|
||||||
.br
|
.br
|
||||||
webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp
|
webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp
|
||||||
.br
|
.br
|
||||||
@@ -169,9 +238,10 @@ webpmux \-get icc \-o image_profile.icc \-\- \-\-\-icc_container.webp
|
|||||||
webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp
|
webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp
|
||||||
|
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
\fBwebpmux\fP is written by the WebP team.
|
\fBwebpmux\fP is a part of libwebp and was written by the WebP team.
|
||||||
.br
|
.br
|
||||||
The latest source tree is available at http://www.webmproject.org/code
|
The latest source tree is available at
|
||||||
|
https://chromium.googlesource.com/webm/libwebp
|
||||||
.PP
|
.PP
|
||||||
This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
|
This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
|
||||||
for the Debian project (and may be used by others).
|
for the Debian project (and may be used by others).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# The mux and demux libraries depend on libwebp, thus the '.' to force the
|
# The mux and demux libraries depend on libwebp, thus the '.' to force
|
||||||
# build order so it's available to them.
|
# the build order so it's available to them.
|
||||||
SUBDIRS = dec enc dsp utils .
|
SUBDIRS = dec enc dsp utils .
|
||||||
if WANT_MUX
|
if WANT_MUX
|
||||||
SUBDIRS += mux
|
SUBDIRS += mux
|
||||||
@@ -35,7 +35,7 @@ libwebp_la_LIBADD += utils/libwebputils.la
|
|||||||
# other than the ones listed on the command line, i.e., after linking, it will
|
# 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
|
# not have unresolved symbols. Some platforms (Windows among them) require all
|
||||||
# symbols in shared libraries to be resolved at library creation.
|
# symbols in shared libraries to be resolved at library creation.
|
||||||
libwebp_la_LDFLAGS = -no-undefined -version-info 5:1:0
|
libwebp_la_LDFLAGS = -no-undefined -version-info 7:0:0
|
||||||
libwebpincludedir = $(includedir)/webp
|
libwebpincludedir = $(includedir)/webp
|
||||||
pkgconfig_DATA = libwebp.pc
|
pkgconfig_DATA = libwebp.pc
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ if BUILD_LIBWEBPDECODER
|
|||||||
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
|
libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
|
||||||
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
|
libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
|
||||||
|
|
||||||
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 1:1:0
|
libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 3:0:0
|
||||||
pkgconfig_DATA += libwebpdecoder.pc
|
pkgconfig_DATA += libwebpdecoder.pc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
noinst_LTLIBRARIES = libwebpdecode.la
|
noinst_LTLIBRARIES = libwebpdecode.la
|
||||||
|
|
||||||
libwebpdecode_la_SOURCES =
|
libwebpdecode_la_SOURCES =
|
||||||
libwebpdecode_la_SOURCES += alpha.c
|
libwebpdecode_la_SOURCES += alpha_dec.c
|
||||||
libwebpdecode_la_SOURCES += alphai.h
|
libwebpdecode_la_SOURCES += alphai_dec.h
|
||||||
libwebpdecode_la_SOURCES += buffer.c
|
libwebpdecode_la_SOURCES += buffer_dec.c
|
||||||
libwebpdecode_la_SOURCES += decode_vp8.h
|
libwebpdecode_la_SOURCES += common_dec.h
|
||||||
libwebpdecode_la_SOURCES += frame.c
|
libwebpdecode_la_SOURCES += vp8_dec.h
|
||||||
libwebpdecode_la_SOURCES += idec.c
|
libwebpdecode_la_SOURCES += frame_dec.c
|
||||||
libwebpdecode_la_SOURCES += io.c
|
libwebpdecode_la_SOURCES += idec_dec.c
|
||||||
libwebpdecode_la_SOURCES += quant.c
|
libwebpdecode_la_SOURCES += io_dec.c
|
||||||
libwebpdecode_la_SOURCES += tree.c
|
libwebpdecode_la_SOURCES += quant_dec.c
|
||||||
libwebpdecode_la_SOURCES += vp8.c
|
libwebpdecode_la_SOURCES += tree_dec.c
|
||||||
libwebpdecode_la_SOURCES += vp8i.h
|
libwebpdecode_la_SOURCES += vp8_dec.c
|
||||||
libwebpdecode_la_SOURCES += vp8l.c
|
libwebpdecode_la_SOURCES += vp8i_dec.h
|
||||||
libwebpdecode_la_SOURCES += vp8li.h
|
libwebpdecode_la_SOURCES += vp8l_dec.c
|
||||||
libwebpdecode_la_SOURCES += webp.c
|
libwebpdecode_la_SOURCES += vp8li_dec.h
|
||||||
libwebpdecode_la_SOURCES += webpi.h
|
libwebpdecode_la_SOURCES += webp_dec.c
|
||||||
|
libwebpdecode_la_SOURCES += webpi_dec.h
|
||||||
|
|
||||||
libwebpdecodeinclude_HEADERS =
|
libwebpdecodeinclude_HEADERS =
|
||||||
libwebpdecodeinclude_HEADERS += ../webp/decode.h
|
libwebpdecodeinclude_HEADERS += ../webp/decode.h
|
||||||
@@ -23,5 +24,5 @@ libwebpdecodeinclude_HEADERS += ../webp/types.h
|
|||||||
noinst_HEADERS =
|
noinst_HEADERS =
|
||||||
noinst_HEADERS += ../webp/format_constants.h
|
noinst_HEADERS += ../webp/format_constants.h
|
||||||
|
|
||||||
libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE)
|
libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||||
libwebpdecodeincludedir = $(includedir)/webp
|
libwebpdecodeincludedir = $(includedir)/webp
|
||||||
|
|||||||
165
src/dec/alpha.c
165
src/dec/alpha.c
@@ -1,165 +0,0 @@
|
|||||||
// 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 "./alphai.h"
|
|
||||||
#include "./vp8i.h"
|
|
||||||
#include "./vp8li.h"
|
|
||||||
#include "../utils/quant_levels_dec.h"
|
|
||||||
#include "../utils/utils.h"
|
|
||||||
#include "../webp/format_constants.h"
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// ALPHDecoder object.
|
|
||||||
|
|
||||||
ALPHDecoder* ALPHNew(void) {
|
|
||||||
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
|
||||||
return dec;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, int width, int height, 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;
|
|
||||||
|
|
||||||
assert(width > 0 && height > 0);
|
|
||||||
assert(data != NULL && output != NULL);
|
|
||||||
|
|
||||||
dec->width_ = width;
|
|
||||||
dec->height_ = height;
|
|
||||||
|
|
||||||
if (data_size <= ALPHA_HEADER_LEN) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dec->method_ = (data[0] >> 0) & 0x03;
|
|
||||||
dec->filter_ = (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, output);
|
|
||||||
}
|
|
||||||
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->height_;
|
|
||||||
WebPUnfilterFunc unfilter_func = WebPUnfilters[alph_dec->filter_];
|
|
||||||
uint8_t* const output = dec->alpha_plane_;
|
|
||||||
if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
|
|
||||||
const size_t offset = row * width;
|
|
||||||
const size_t num_pixels = num_rows * width;
|
|
||||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN + offset + num_pixels);
|
|
||||||
memcpy(dec->alpha_plane_ + offset,
|
|
||||||
dec->alpha_data_ + ALPHA_HEADER_LEN + offset, num_pixels);
|
|
||||||
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
|
|
||||||
assert(alph_dec->vp8l_dec_ != NULL);
|
|
||||||
if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unfilter_func != NULL) {
|
|
||||||
unfilter_func(width, height, width, row, num_rows, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row + num_rows == dec->pic_hdr_.height_) {
|
|
||||||
dec->is_alpha_decoded_ = 1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Main entry point.
|
|
||||||
|
|
||||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
|
||||||
int row, int num_rows) {
|
|
||||||
const int width = dec->pic_hdr_.width_;
|
|
||||||
const int height = dec->pic_hdr_.height_;
|
|
||||||
|
|
||||||
if (row < 0 || num_rows <= 0 || row + num_rows > height) {
|
|
||||||
return NULL; // sanity check.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row == 0) {
|
|
||||||
// Initialize decoding.
|
|
||||||
assert(dec->alpha_plane_ != NULL);
|
|
||||||
dec->alph_dec_ = ALPHNew();
|
|
||||||
if (dec->alph_dec_ == NULL) return NULL;
|
|
||||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
|
||||||
width, height, dec->alpha_plane_)) {
|
|
||||||
ALPHDelete(dec->alph_dec_);
|
|
||||||
dec->alph_dec_ = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
// 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; // decode everything in one pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dec->is_alpha_decoded_) {
|
|
||||||
int ok = 0;
|
|
||||||
assert(dec->alph_dec_ != NULL);
|
|
||||||
ok = ALPHDecode(dec, row, num_rows);
|
|
||||||
if (ok && dec->alpha_dithering_ > 0) {
|
|
||||||
ok = WebPDequantizeLevels(dec->alpha_plane_, width, height,
|
|
||||||
dec->alpha_dithering_);
|
|
||||||
}
|
|
||||||
if (!ok || dec->is_alpha_decoded_) {
|
|
||||||
ALPHDelete(dec->alph_dec_);
|
|
||||||
dec->alph_dec_ = NULL;
|
|
||||||
}
|
|
||||||
if (!ok) return NULL; // Error.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a pointer to the current decoded row.
|
|
||||||
return dec->alpha_plane_ + row * width;
|
|
||||||
}
|
|
||||||
232
src/dec/alpha_dec.c
Normal file
232
src/dec/alpha_dec.c
Normal file
@@ -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 "./alphai_dec.h"
|
||||||
|
#include "./vp8i_dec.h"
|
||||||
|
#include "./vp8li_dec.h"
|
||||||
|
#include "../dsp/dsp.h"
|
||||||
|
#include "../utils/quant_levels_dec_utils.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
#include "../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; // sanity check.
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
#ifndef WEBP_DEC_ALPHAI_H_
|
#ifndef WEBP_DEC_ALPHAI_H_
|
||||||
#define WEBP_DEC_ALPHAI_H_
|
#define WEBP_DEC_ALPHAI_H_
|
||||||
|
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "../utils/filters.h"
|
#include "../utils/filters_utils.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -32,19 +32,18 @@ struct ALPHDecoder {
|
|||||||
int pre_processing_;
|
int pre_processing_;
|
||||||
struct VP8LDecoder* vp8l_dec_;
|
struct VP8LDecoder* vp8l_dec_;
|
||||||
VP8Io io_;
|
VP8Io io_;
|
||||||
int use_8b_decode; // Although alpha channel requires only 1 byte per
|
int use_8b_decode_; // Although alpha channel requires only 1 byte per
|
||||||
// pixel, sometimes VP8LDecoder may need to allocate
|
// pixel, sometimes VP8LDecoder may need to allocate
|
||||||
// 4 bytes per pixel internally during decode.
|
// 4 bytes per pixel internally during decode.
|
||||||
|
uint8_t* output_;
|
||||||
|
const uint8_t* prev_line_; // last output row (or NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// internal functions. Not public.
|
// internal functions. Not public.
|
||||||
|
|
||||||
// Allocates a new alpha decoder instance.
|
// Deallocate memory associated to dec->alpha_plane_ decoding
|
||||||
ALPHDecoder* ALPHNew(void);
|
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
|
||||||
|
|
||||||
// Clears and deallocates an alpha decoder instance.
|
|
||||||
void ALPHDelete(ALPHDecoder* const dec);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -33,6 +33,11 @@ static int IsValidColorspace(int webp_csp_mode) {
|
|||||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
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) {
|
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||||
@@ -42,20 +47,22 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
|||||||
ok = 0;
|
ok = 0;
|
||||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
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 y_stride = abs(buf->y_stride);
|
||||||
const int u_stride = abs(buf->u_stride);
|
const int u_stride = abs(buf->u_stride);
|
||||||
const int v_stride = abs(buf->v_stride);
|
const int v_stride = abs(buf->v_stride);
|
||||||
const int a_stride = abs(buf->a_stride);
|
const int a_stride = abs(buf->a_stride);
|
||||||
const uint64_t y_size = (uint64_t)y_stride * height;
|
const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
|
||||||
const uint64_t u_size = (uint64_t)u_stride * ((height + 1) / 2);
|
const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
|
||||||
const uint64_t v_size = (uint64_t)v_stride * ((height + 1) / 2);
|
const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
|
||||||
const uint64_t a_size = (uint64_t)a_stride * height;
|
const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
|
||||||
ok &= (y_size <= buf->y_size);
|
ok &= (y_size <= buf->y_size);
|
||||||
ok &= (u_size <= buf->u_size);
|
ok &= (u_size <= buf->u_size);
|
||||||
ok &= (v_size <= buf->v_size);
|
ok &= (v_size <= buf->v_size);
|
||||||
ok &= (y_stride >= width);
|
ok &= (y_stride >= width);
|
||||||
ok &= (u_stride >= (width + 1) / 2);
|
ok &= (u_stride >= uv_width);
|
||||||
ok &= (v_stride >= (width + 1) / 2);
|
ok &= (v_stride >= uv_width);
|
||||||
ok &= (buf->y != NULL);
|
ok &= (buf->y != NULL);
|
||||||
ok &= (buf->u != NULL);
|
ok &= (buf->u != NULL);
|
||||||
ok &= (buf->v != NULL);
|
ok &= (buf->v != NULL);
|
||||||
@@ -67,13 +74,14 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
|||||||
} else { // RGB checks
|
} else { // RGB checks
|
||||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||||
const int stride = abs(buf->stride);
|
const int stride = abs(buf->stride);
|
||||||
const uint64_t size = (uint64_t)stride * height;
|
const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
|
||||||
ok &= (size <= buf->size);
|
ok &= (size <= buf->size);
|
||||||
ok &= (stride >= width * kModeBpp[mode]);
|
ok &= (stride >= width * kModeBpp[mode]);
|
||||||
ok &= (buf->rgba != NULL);
|
ok &= (buf->rgba != NULL);
|
||||||
}
|
}
|
||||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
#undef MIN_BUFFER_SIZE
|
||||||
|
|
||||||
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||||
const int w = buffer->width;
|
const int w = buffer->width;
|
||||||
@@ -84,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
|||||||
return VP8_STATUS_INVALID_PARAM;
|
return VP8_STATUS_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!buffer->is_external_memory && buffer->private_memory == NULL) {
|
if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) {
|
||||||
uint8_t* output;
|
uint8_t* output;
|
||||||
int uv_stride = 0, a_stride = 0;
|
int uv_stride = 0, a_stride = 0;
|
||||||
uint64_t uv_size = 0, a_size = 0, total_size;
|
uint64_t uv_size = 0, a_size = 0, total_size;
|
||||||
@@ -181,11 +189,14 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
|||||||
h = ch;
|
h = ch;
|
||||||
}
|
}
|
||||||
if (options->use_scaling) {
|
if (options->use_scaling) {
|
||||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
int scaled_width = options->scaled_width;
|
||||||
|
int scaled_height = options->scaled_height;
|
||||||
|
if (!WebPRescalerGetScaledDimensions(
|
||||||
|
w, h, &scaled_width, &scaled_height)) {
|
||||||
return VP8_STATUS_INVALID_PARAM;
|
return VP8_STATUS_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
w = options->scaled_width;
|
w = scaled_width;
|
||||||
h = options->scaled_height;
|
h = scaled_height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out->width = w;
|
out->width = w;
|
||||||
@@ -195,12 +206,10 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
|||||||
status = AllocateBuffer(out);
|
status = AllocateBuffer(out);
|
||||||
if (status != VP8_STATUS_OK) return status;
|
if (status != VP8_STATUS_OK) return status;
|
||||||
|
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
// Use the stride trick if vertical flip is needed.
|
// Use the stride trick if vertical flip is needed.
|
||||||
if (options != NULL && options->flip) {
|
if (options != NULL && options->flip) {
|
||||||
status = WebPFlipBuffer(out);
|
status = WebPFlipBuffer(out);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
|||||||
|
|
||||||
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
||||||
if (buffer != NULL) {
|
if (buffer != NULL) {
|
||||||
if (!buffer->is_external_memory) {
|
if (buffer->is_external_memory <= 0) {
|
||||||
WebPSafeFree(buffer->private_memory);
|
WebPSafeFree(buffer->private_memory);
|
||||||
}
|
}
|
||||||
buffer->private_memory = NULL;
|
buffer->private_memory = NULL;
|
||||||
@@ -247,5 +256,45 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
54
src/dec/common_dec.h
Normal file
54
src/dec/common_dec.h
Normal file
@@ -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_H_
|
||||||
|
#define WEBP_DEC_COMMON_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_H_
|
||||||
@@ -12,13 +12,183 @@
|
|||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
#define ALIGN_MASK (32 - 1)
|
//------------------------------------------------------------------------------
|
||||||
|
// Main reconstruction function.
|
||||||
|
|
||||||
|
static const int 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,
|
static void ReconstructRow(const VP8Decoder* const dec,
|
||||||
const VP8ThreadContext* ctx); // TODO(skal): remove
|
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
|
// Filtering
|
||||||
@@ -112,7 +282,6 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
|||||||
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
||||||
int level = base_level;
|
int level = base_level;
|
||||||
if (hdr->use_lf_delta_) {
|
if (hdr->use_lf_delta_) {
|
||||||
// TODO(skal): only CURRENT is handled for now.
|
|
||||||
level += hdr->ref_lf_delta_[0];
|
level += hdr->ref_lf_delta_[0];
|
||||||
if (i4x4) {
|
if (i4x4) {
|
||||||
level += hdr->mode_lf_delta_[0];
|
level += hdr->mode_lf_delta_[0];
|
||||||
@@ -147,6 +316,9 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Dithering
|
// Dithering
|
||||||
|
|
||||||
|
// minimal amp that will provide a non-zero dithering effect
|
||||||
|
#define MIN_DITHER_AMP 4
|
||||||
|
|
||||||
#define DITHER_AMP_TAB_SIZE 12
|
#define DITHER_AMP_TAB_SIZE 12
|
||||||
static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
||||||
// roughly, it's dqm->uv_mat_[1]
|
// roughly, it's dqm->uv_mat_[1]
|
||||||
@@ -177,7 +349,6 @@ void VP8InitDithering(const WebPDecoderOptions* const options,
|
|||||||
dec->dither_ = 1;
|
dec->dither_ = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
// potentially allow alpha dithering
|
// potentially allow alpha dithering
|
||||||
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
||||||
if (dec->alpha_dithering_ > 100) {
|
if (dec->alpha_dithering_ > 100) {
|
||||||
@@ -185,31 +356,17 @@ void VP8InitDithering(const WebPDecoderOptions* const options,
|
|||||||
} else if (dec->alpha_dithering_ < 0) {
|
} else if (dec->alpha_dithering_ < 0) {
|
||||||
dec->alpha_dithering_ = 0;
|
dec->alpha_dithering_ = 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// minimal amp that will provide a non-zero dithering effect
|
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
|
||||||
#define MIN_DITHER_AMP 4
|
|
||||||
#define DITHER_DESCALE 4
|
|
||||||
#define DITHER_DESCALE_ROUNDER (1 << (DITHER_DESCALE - 1))
|
|
||||||
#define DITHER_AMP_BITS 8
|
|
||||||
#define DITHER_AMP_CENTER (1 << DITHER_AMP_BITS)
|
|
||||||
|
|
||||||
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
||||||
int i, j;
|
uint8_t dither[64];
|
||||||
for (j = 0; j < 8; ++j) {
|
int i;
|
||||||
for (i = 0; i < 8; ++i) {
|
for (i = 0; i < 8 * 8; ++i) {
|
||||||
// TODO: could be made faster with SSE2
|
dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp);
|
||||||
const int bits =
|
|
||||||
VP8RandomBits2(rg, DITHER_AMP_BITS + 1, amp) - DITHER_AMP_CENTER;
|
|
||||||
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
|
|
||||||
const int delta = (bits + DITHER_DESCALE_ROUNDER) >> DITHER_DESCALE;
|
|
||||||
const int v = (int)dst[i] + delta;
|
|
||||||
dst[i] = (v < 0) ? 0 : (v > 255) ? 255u : (uint8_t)v;
|
|
||||||
}
|
|
||||||
dst += bps;
|
|
||||||
}
|
}
|
||||||
|
VP8DitherCombine8x8(dither, dst, bps);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DitherRow(VP8Decoder* const dec) {
|
static void DitherRow(VP8Decoder* const dec) {
|
||||||
@@ -295,7 +452,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||||
// TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
|
// TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
|
||||||
// good idea.
|
// good idea.
|
||||||
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
|
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
|
||||||
if (io->a == NULL) {
|
if (io->a == NULL) {
|
||||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||||
"Could not decode alpha data.");
|
"Could not decode alpha data.");
|
||||||
@@ -554,7 +711,7 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
|||||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||||
+ top_size + mb_info_size + f_info_size
|
+ top_size + mb_info_size + f_info_size
|
||||||
+ yuv_size + mb_data_size
|
+ yuv_size + mb_data_size
|
||||||
+ cache_size + alpha_size + ALIGN_MASK;
|
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||||
uint8_t* mem;
|
uint8_t* mem;
|
||||||
|
|
||||||
if (needed != (size_t)needed) return 0; // check for overflow
|
if (needed != (size_t)needed) return 0; // check for overflow
|
||||||
@@ -566,7 +723,7 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
|||||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||||
"no memory during frame initialization.");
|
"no memory during frame initialization.");
|
||||||
}
|
}
|
||||||
// down-cast is ok, thanks to WebPSafeAlloc() above.
|
// down-cast is ok, thanks to WebPSafeMalloc() above.
|
||||||
dec->mem_size_ = (size_t)needed;
|
dec->mem_size_ = (size_t)needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,8 +748,8 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
|||||||
dec->thread_ctx_.f_info_ += mb_w;
|
dec->thread_ctx_.f_info_ += mb_w;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
mem = (uint8_t*)WEBP_ALIGN(mem);
|
||||||
assert((yuv_size & ALIGN_MASK) == 0);
|
assert((yuv_size & WEBP_ALIGN_CST) == 0);
|
||||||
dec->yuv_b_ = (uint8_t*)mem;
|
dec->yuv_b_ = (uint8_t*)mem;
|
||||||
mem += yuv_size;
|
mem += yuv_size;
|
||||||
|
|
||||||
@@ -644,7 +801,7 @@ static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
|||||||
io->a = NULL;
|
io->a = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
|
||||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||||
if (!AllocateMemory(dec)) return 0;
|
if (!AllocateMemory(dec)) return 0;
|
||||||
InitIo(dec, io);
|
InitIo(dec, io);
|
||||||
@@ -653,176 +810,3 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Main reconstruction function.
|
|
||||||
|
|
||||||
static const int 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* dst, uint8_t* 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;
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
} else if (mb_x == 0) {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./alphai.h"
|
#include "./alphai_dec.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
// In append mode, buffer allocations increase as multiples of this value.
|
// In append mode, buffer allocations increase as multiples of this value.
|
||||||
@@ -70,7 +70,9 @@ struct WebPIDecoder {
|
|||||||
VP8Io io_;
|
VP8Io io_;
|
||||||
|
|
||||||
MemBuffer mem_; // input memory buffer.
|
MemBuffer mem_; // input memory buffer.
|
||||||
WebPDecBuffer output_; // output buffer (when no external one is supplied)
|
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.
|
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
|
||||||
|
|
||||||
int last_mb_y_; // last row reached for intra-mode decoding
|
int last_mb_y_; // last row reached for intra-mode decoding
|
||||||
@@ -118,9 +120,9 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
|||||||
if (idec->dec_ != NULL) {
|
if (idec->dec_ != NULL) {
|
||||||
if (!idec->is_lossless_) {
|
if (!idec->is_lossless_) {
|
||||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||||
const int last_part = dec->num_parts_ - 1;
|
const uint32_t last_part = dec->num_parts_minus_one_;
|
||||||
if (offset != 0) {
|
if (offset != 0) {
|
||||||
int p;
|
uint32_t p;
|
||||||
for (p = 0; p <= last_part; ++p) {
|
for (p = 0; p <= last_part; ++p) {
|
||||||
VP8RemapBitReader(dec->parts_ + p, offset);
|
VP8RemapBitReader(dec->parts_ + p, offset);
|
||||||
}
|
}
|
||||||
@@ -130,8 +132,11 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
|||||||
VP8RemapBitReader(&dec->br_, offset);
|
VP8RemapBitReader(&dec->br_, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(last_part >= 0);
|
{
|
||||||
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
|
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)) {
|
if (NeedCompressedAlpha(idec)) {
|
||||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||||
dec->alpha_data_ += offset;
|
dec->alpha_data_ += offset;
|
||||||
@@ -240,16 +245,20 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
|||||||
|
|
||||||
// To be called last.
|
// To be called last.
|
||||||
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
const WebPDecoderOptions* const options = idec->params_.options;
|
const WebPDecoderOptions* const options = idec->params_.options;
|
||||||
WebPDecBuffer* const output = idec->params_.output;
|
WebPDecBuffer* const output = idec->params_.output;
|
||||||
|
|
||||||
idec->state_ = STATE_DONE;
|
idec->state_ = STATE_DONE;
|
||||||
if (options != NULL && options->flip) {
|
if (options != NULL && options->flip) {
|
||||||
return WebPFlipBuffer(output);
|
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;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
idec->state_ = STATE_DONE;
|
|
||||||
return VP8_STATUS_OK;
|
return VP8_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,30 +366,32 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Partition #0
|
// Partition #0
|
||||||
static int CopyParts0Data(WebPIDecoder* const idec) {
|
static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
|
||||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||||
VP8BitReader* const br = &dec->br_;
|
VP8BitReader* const br = &dec->br_;
|
||||||
const size_t psize = br->buf_end_ - br->buf_;
|
const size_t part_size = br->buf_end_ - br->buf_;
|
||||||
MemBuffer* const mem = &idec->mem_;
|
MemBuffer* const mem = &idec->mem_;
|
||||||
assert(!idec->is_lossless_);
|
assert(!idec->is_lossless_);
|
||||||
assert(mem->part0_buf_ == NULL);
|
assert(mem->part0_buf_ == NULL);
|
||||||
assert(psize > 0);
|
// the following is a format limitation, no need for runtime check:
|
||||||
assert(psize <= mem->part0_size_); // Format limit: 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) {
|
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||||
// We copy and grab ownership of the partition #0 data.
|
// We copy and grab ownership of the partition #0 data.
|
||||||
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, psize);
|
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
|
||||||
if (part0_buf == NULL) {
|
if (part0_buf == NULL) {
|
||||||
return 0;
|
return VP8_STATUS_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
memcpy(part0_buf, br->buf_, psize);
|
memcpy(part0_buf, br->buf_, part_size);
|
||||||
mem->part0_buf_ = part0_buf;
|
mem->part0_buf_ = part0_buf;
|
||||||
br->buf_ = part0_buf;
|
VP8BitReaderSetBuffer(br, part0_buf, part_size);
|
||||||
br->buf_end_ = part0_buf + psize;
|
|
||||||
} else {
|
} else {
|
||||||
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
||||||
}
|
}
|
||||||
mem->start_ += psize;
|
mem->start_ += part_size;
|
||||||
return 1;
|
return VP8_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||||
@@ -414,8 +425,10 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
|||||||
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
|
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
|
||||||
io->width, io->height);
|
io->width, io->height);
|
||||||
VP8InitDithering(params->options, dec);
|
VP8InitDithering(params->options, dec);
|
||||||
if (!CopyParts0Data(idec)) {
|
|
||||||
return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
|
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().
|
// Finish setting up the decoding parameters. Will call io->setup().
|
||||||
@@ -451,19 +464,20 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
|||||||
}
|
}
|
||||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||||
VP8BitReader* const token_br =
|
VP8BitReader* const token_br =
|
||||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||||
MBContext context;
|
MBContext context;
|
||||||
SaveContext(dec, token_br, &context);
|
SaveContext(dec, token_br, &context);
|
||||||
if (!VP8DecodeMB(dec, token_br)) {
|
if (!VP8DecodeMB(dec, token_br)) {
|
||||||
// We shouldn't fail when MAX_MB data was available
|
// We shouldn't fail when MAX_MB data was available
|
||||||
if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
|
if (dec->num_parts_minus_one_ == 0 &&
|
||||||
|
MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
|
||||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||||
}
|
}
|
||||||
RestoreContext(&context, dec, token_br);
|
RestoreContext(&context, dec, token_br);
|
||||||
return VP8_STATUS_SUSPENDED;
|
return VP8_STATUS_SUSPENDED;
|
||||||
}
|
}
|
||||||
// Release buffer only if there is only one partition
|
// Release buffer only if there is only one partition
|
||||||
if (dec->num_parts_ == 1) {
|
if (dec->num_parts_minus_one_ == 0) {
|
||||||
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
|
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
|
||||||
assert(idec->mem_.start_ <= idec->mem_.end_);
|
assert(idec->mem_.start_ <= idec->mem_.end_);
|
||||||
}
|
}
|
||||||
@@ -501,9 +515,15 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
|
|||||||
|
|
||||||
// Wait until there's enough data for decoding header.
|
// Wait until there's enough data for decoding header.
|
||||||
if (curr_size < (idec->chunk_size_ >> 3)) {
|
if (curr_size < (idec->chunk_size_ >> 3)) {
|
||||||
return VP8_STATUS_SUSPENDED;
|
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||||
|
return ErrorStatusLossless(idec, dec->status_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VP8LDecodeHeader(dec, io)) {
|
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_);
|
return ErrorStatusLossless(idec, dec->status_);
|
||||||
}
|
}
|
||||||
// Allocate/verify output buffer now.
|
// Allocate/verify output buffer now.
|
||||||
@@ -522,17 +542,15 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
|
|||||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||||
assert(idec->is_lossless_);
|
assert(idec->is_lossless_);
|
||||||
|
|
||||||
// At present Lossless decoder can't decode image incrementally. So wait till
|
// Switch to incremental decoding if we don't have all the bytes available.
|
||||||
// all the image data is aggregated before image can be decoded.
|
dec->incremental_ = (curr_size < idec->chunk_size_);
|
||||||
if (curr_size < idec->chunk_size_) {
|
|
||||||
return VP8_STATUS_SUSPENDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!VP8LDecodeImage(dec)) {
|
if (!VP8LDecodeImage(dec)) {
|
||||||
return ErrorStatusLossless(idec, dec->status_);
|
return ErrorStatusLossless(idec, dec->status_);
|
||||||
}
|
}
|
||||||
|
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
|
||||||
return FinishDecoding(idec);
|
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
|
||||||
|
: FinishDecoding(idec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main decoding loop
|
// Main decoding loop
|
||||||
@@ -565,9 +583,10 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Public functions
|
// Internal constructor
|
||||||
|
|
||||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
|
||||||
|
const WebPBitstreamFeatures* const features) {
|
||||||
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
||||||
if (idec == NULL) {
|
if (idec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -583,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
|||||||
VP8InitIo(&idec->io_);
|
VP8InitIo(&idec->io_);
|
||||||
|
|
||||||
WebPResetDecParams(&idec->params_);
|
WebPResetDecParams(&idec->params_);
|
||||||
idec->params_.output = (output_buffer != NULL) ? output_buffer
|
if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
|
||||||
: &idec->output_;
|
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.
|
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
|
||||||
|
|
||||||
return idec;
|
return idec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||||
|
return NewDecoder(output_buffer, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
||||||
WebPDecoderConfig* config) {
|
WebPDecoderConfig* config) {
|
||||||
WebPIDecoder* idec;
|
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:
|
// Parse the bitstream's features, if requested:
|
||||||
if (data != NULL && data_size > 0 && config != NULL) {
|
if (data != NULL && data_size > 0) {
|
||||||
if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
|
if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an instance of the incremental decoder
|
// Create an instance of the incremental decoder
|
||||||
idec = WebPINewDecoder(config ? &config->output : NULL);
|
idec = (config != NULL) ? NewDecoder(&config->output, features)
|
||||||
|
: NewDecoder(NULL, features);
|
||||||
if (idec == NULL) {
|
if (idec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -635,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) {
|
|||||||
|
|
||||||
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
||||||
size_t output_buffer_size, int output_stride) {
|
size_t output_buffer_size, int output_stride) {
|
||||||
const int is_external_memory = (output_buffer != NULL);
|
const int is_external_memory = (output_buffer != NULL) ? 1 : 0;
|
||||||
WebPIDecoder* idec;
|
WebPIDecoder* idec;
|
||||||
|
|
||||||
if (mode >= MODE_YUV) return NULL;
|
if (mode >= MODE_YUV) return NULL;
|
||||||
if (!is_external_memory) { // Overwrite parameters to sane values.
|
if (is_external_memory == 0) { // Overwrite parameters to sane values.
|
||||||
output_buffer_size = 0;
|
output_buffer_size = 0;
|
||||||
output_stride = 0;
|
output_stride = 0;
|
||||||
} else { // A buffer was passed. Validate the other params.
|
} else { // A buffer was passed. Validate the other params.
|
||||||
@@ -661,11 +701,11 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
|
|||||||
uint8_t* u, size_t u_size, int u_stride,
|
uint8_t* u, size_t u_size, int u_stride,
|
||||||
uint8_t* v, size_t v_size, int v_stride,
|
uint8_t* v, size_t v_size, int v_stride,
|
||||||
uint8_t* a, size_t a_size, int a_stride) {
|
uint8_t* a, size_t a_size, int a_stride) {
|
||||||
const int is_external_memory = (luma != NULL);
|
const int is_external_memory = (luma != NULL) ? 1 : 0;
|
||||||
WebPIDecoder* idec;
|
WebPIDecoder* idec;
|
||||||
WEBP_CSP_MODE colorspace;
|
WEBP_CSP_MODE colorspace;
|
||||||
|
|
||||||
if (!is_external_memory) { // Overwrite parameters to sane values.
|
if (is_external_memory == 0) { // Overwrite parameters to sane values.
|
||||||
luma_size = u_size = v_size = a_size = 0;
|
luma_size = u_size = v_size = a_size = 0;
|
||||||
luma_stride = u_stride = v_stride = a_stride = 0;
|
luma_stride = u_stride = v_stride = a_stride = 0;
|
||||||
u = v = a = NULL;
|
u = v = a = NULL;
|
||||||
@@ -773,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
|
|||||||
if (idec->state_ <= STATE_VP8_PARTS0) {
|
if (idec->state_ <= STATE_VP8_PARTS0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (idec->final_output_ != NULL) {
|
||||||
|
return NULL; // not yet slow-copied
|
||||||
|
}
|
||||||
return idec->params_.output;
|
return idec->params_.output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,8 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
|||||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||||
if (left != NULL) *left = 0;
|
if (left != NULL) *left = 0;
|
||||||
if (top != NULL) *top = 0;
|
if (top != NULL) *top = 0;
|
||||||
// TODO(skal): later include handling of rotations.
|
if (src != NULL) {
|
||||||
if (src) {
|
|
||||||
if (width != NULL) *width = src->width;
|
if (width != NULL) *width = src->width;
|
||||||
if (height != NULL) *height = idec->params_.last_y;
|
if (height != NULL) *height = idec->params_.last_y;
|
||||||
} else {
|
} else {
|
||||||
@@ -848,4 +890,3 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../dec/vp8i.h"
|
#include "../dec/vp8i_dec.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "../dsp/dsp.h"
|
#include "../dsp/dsp.h"
|
||||||
#include "../dsp/yuv.h"
|
#include "../dsp/yuv.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
@@ -55,32 +55,6 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
return io->mb_h;
|
return io->mb_h;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// YUV444 -> RGB conversion
|
|
||||||
|
|
||||||
#if 0 // TODO(skal): this is for future rescaling.
|
|
||||||
static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|
||||||
WebPDecBuffer* output = p->output;
|
|
||||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
|
||||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
|
||||||
const uint8_t* y_src = io->y;
|
|
||||||
const uint8_t* u_src = io->u;
|
|
||||||
const uint8_t* v_src = io->v;
|
|
||||||
const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
|
|
||||||
const int mb_w = io->mb_w;
|
|
||||||
const int last = io->mb_h;
|
|
||||||
int j;
|
|
||||||
for (j = 0; j < last; ++j) {
|
|
||||||
convert(y_src, u_src, v_src, dst, mb_w);
|
|
||||||
y_src += io->y_stride;
|
|
||||||
u_src += io->uv_stride;
|
|
||||||
v_src += io->uv_stride;
|
|
||||||
dst += buf->stride;
|
|
||||||
}
|
|
||||||
return io->mb_h;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Fancy upsampling
|
// Fancy upsampling
|
||||||
|
|
||||||
@@ -145,14 +119,24 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
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 uint8_t* alpha = io->a;
|
||||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
const int mb_h = io->mb_h;
|
const int mb_h = io->mb_h;
|
||||||
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
||||||
int j;
|
int j;
|
||||||
|
(void)expected_num_lines_out;
|
||||||
|
assert(expected_num_lines_out == mb_h);
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
for (j = 0; j < mb_h; ++j) {
|
for (j = 0; j < mb_h; ++j) {
|
||||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||||
@@ -161,10 +145,7 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
}
|
}
|
||||||
} else if (buf->a != NULL) {
|
} else if (buf->a != NULL) {
|
||||||
// the user requested alpha, but there is none, set it to opaque.
|
// the user requested alpha, but there is none, set it to opaque.
|
||||||
for (j = 0; j < mb_h; ++j) {
|
FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride);
|
||||||
memset(dst, 0xff, mb_w * sizeof(*dst));
|
|
||||||
dst += buf->a_stride;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -195,7 +176,8 @@ static int GetAlphaSourceRow(const VP8Io* const io,
|
|||||||
return start_y;
|
return start_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||||
|
int expected_num_lines_out) {
|
||||||
const uint8_t* alpha = io->a;
|
const uint8_t* alpha = io->a;
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
@@ -206,21 +188,13 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
int num_rows;
|
int num_rows;
|
||||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
|
||||||
uint32_t alpha_mask = 0xff;
|
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
|
||||||
int i, j;
|
num_rows, dst, buf->stride);
|
||||||
|
(void)expected_num_lines_out;
|
||||||
for (j = 0; j < num_rows; ++j) {
|
assert(expected_num_lines_out == num_rows);
|
||||||
for (i = 0; i < mb_w; ++i) {
|
// has_alpha is true if there's non-trivial alpha to premultiply with.
|
||||||
const uint32_t alpha_value = alpha[i];
|
if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||||
dst[4 * i] = alpha_value;
|
|
||||||
alpha_mask &= alpha_value;
|
|
||||||
}
|
|
||||||
alpha += io->width;
|
|
||||||
dst += buf->stride;
|
|
||||||
}
|
|
||||||
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
|
|
||||||
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
|
|
||||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||||
mb_w, num_rows, buf->stride);
|
mb_w, num_rows, buf->stride);
|
||||||
}
|
}
|
||||||
@@ -228,7 +202,8 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
|
||||||
|
int expected_num_lines_out) {
|
||||||
const uint8_t* alpha = io->a;
|
const uint8_t* alpha = io->a;
|
||||||
if (alpha != NULL) {
|
if (alpha != NULL) {
|
||||||
const int mb_w = io->mb_w;
|
const int mb_w = io->mb_w;
|
||||||
@@ -244,7 +219,6 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
#endif
|
#endif
|
||||||
uint32_t alpha_mask = 0x0f;
|
uint32_t alpha_mask = 0x0f;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
for (j = 0; j < num_rows; ++j) {
|
for (j = 0; j < num_rows; ++j) {
|
||||||
for (i = 0; i < mb_w; ++i) {
|
for (i = 0; i < mb_w; ++i) {
|
||||||
// Fill in the alpha value (converted to 4 bits).
|
// Fill in the alpha value (converted to 4 bits).
|
||||||
@@ -255,6 +229,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
alpha += io->width;
|
alpha += io->width;
|
||||||
alpha_dst += buf->stride;
|
alpha_dst += buf->stride;
|
||||||
}
|
}
|
||||||
|
(void)expected_num_lines_out;
|
||||||
|
assert(expected_num_lines_out == num_rows);
|
||||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||||
}
|
}
|
||||||
@@ -280,7 +256,7 @@ static int Rescale(const uint8_t* src, int src_stride,
|
|||||||
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||||
const int mb_h = io->mb_h;
|
const int mb_h = io->mb_h;
|
||||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||||
WebPRescaler* const scaler = &p->scaler_y;
|
WebPRescaler* const scaler = p->scaler_y;
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
||||||
// Before rescaling, we premultiply the luma directly into the io->y
|
// Before rescaling, we premultiply the luma directly into the io->y
|
||||||
@@ -291,21 +267,28 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
io->a, io->width, io->mb_w, mb_h, 0);
|
io->a, io->width, io->mb_w, mb_h, 0);
|
||||||
}
|
}
|
||||||
num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
|
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->u, io->uv_stride, uv_mb_h, p->scaler_u);
|
||||||
Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
|
Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v);
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||||
if (io->a != NULL) {
|
int expected_num_lines_out) {
|
||||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||||
uint8_t* dst_y = buf->y + p->last_y * buf->y_stride;
|
uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride;
|
||||||
const uint8_t* src_a = buf->a + p->last_y * buf->a_stride;
|
if (io->a != NULL) {
|
||||||
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
|
uint8_t* const dst_y = buf->y + 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
|
if (num_lines_out > 0) { // unmultiply the Y
|
||||||
WebPMultRows(dst_y, buf->y_stride, src_a, buf->a_stride,
|
WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride,
|
||||||
p->scaler_a.dst_width, num_lines_out, 1);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -321,38 +304,43 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
const int uv_in_height = (io->mb_h + 1) >> 1;
|
const int uv_in_height = (io->mb_h + 1) >> 1;
|
||||||
const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
|
const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
|
||||||
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
|
const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
|
||||||
size_t tmp_size;
|
size_t tmp_size, rescaler_size;
|
||||||
int32_t* work;
|
rescaler_t* work;
|
||||||
|
WebPRescaler* scalers;
|
||||||
|
const int num_rescalers = has_alpha ? 4 : 3;
|
||||||
|
|
||||||
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
|
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
|
||||||
if (has_alpha) {
|
if (has_alpha) {
|
||||||
tmp_size += work_size * sizeof(*work);
|
tmp_size += work_size * sizeof(*work);
|
||||||
}
|
}
|
||||||
p->memory = WebPSafeCalloc(1ULL, tmp_size);
|
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||||
|
|
||||||
|
p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size);
|
||||||
if (p->memory == NULL) {
|
if (p->memory == NULL) {
|
||||||
return 0; // memory error
|
return 0; // memory error
|
||||||
}
|
}
|
||||||
work = (int32_t*)p->memory;
|
work = (rescaler_t*)p->memory;
|
||||||
WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
|
|
||||||
|
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size);
|
||||||
|
p->scaler_y = &scalers[0];
|
||||||
|
p->scaler_u = &scalers[1];
|
||||||
|
p->scaler_v = &scalers[2];
|
||||||
|
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||||
|
|
||||||
|
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||||
buf->y, out_width, out_height, buf->y_stride, 1,
|
buf->y, out_width, out_height, buf->y_stride, 1,
|
||||||
io->mb_w, out_width, io->mb_h, out_height,
|
|
||||||
work);
|
work);
|
||||||
WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
|
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||||
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
|
buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
|
||||||
uv_in_width, uv_out_width,
|
|
||||||
uv_in_height, uv_out_height,
|
|
||||||
work + work_size);
|
work + work_size);
|
||||||
WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
|
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
|
||||||
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
|
buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
|
||||||
uv_in_width, uv_out_width,
|
|
||||||
uv_in_height, uv_out_height,
|
|
||||||
work + work_size + uv_work_size);
|
work + work_size + uv_work_size);
|
||||||
p->emit = EmitRescaledYUV;
|
p->emit = EmitRescaledYUV;
|
||||||
|
|
||||||
if (has_alpha) {
|
if (has_alpha) {
|
||||||
WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
|
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||||
buf->a, out_width, out_height, buf->a_stride, 1,
|
buf->a, out_width, out_height, buf->a_stride, 1,
|
||||||
io->mb_w, out_width, io->mb_h, out_height,
|
|
||||||
work + work_size + 2 * uv_work_size);
|
work + work_size + 2 * uv_work_size);
|
||||||
p->emit_alpha = EmitRescaledAlphaYUV;
|
p->emit_alpha = EmitRescaledAlphaYUV;
|
||||||
WebPInitAlphaProcessing();
|
WebPInitAlphaProcessing();
|
||||||
@@ -367,19 +355,19 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
|||||||
const WebPYUV444Converter convert =
|
const WebPYUV444Converter convert =
|
||||||
WebPYUV444Converters[p->output->colorspace];
|
WebPYUV444Converters[p->output->colorspace];
|
||||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
uint8_t* dst = buf->rgba + y_pos * buf->stride;
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
// For RGB rescaling, because of the YUV420, current scan position
|
// 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.
|
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||||
while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
|
while (WebPRescalerHasPendingOutput(p->scaler_y) &&
|
||||||
WebPRescalerHasPendingOutput(&p->scaler_u)) {
|
WebPRescalerHasPendingOutput(p->scaler_u)) {
|
||||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
assert(y_pos + num_lines_out < p->output->height);
|
||||||
assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
|
assert(p->scaler_u->y_accum == p->scaler_v->y_accum);
|
||||||
WebPRescalerExportRow(&p->scaler_y, 0);
|
WebPRescalerExportRow(p->scaler_y);
|
||||||
WebPRescalerExportRow(&p->scaler_u, 0);
|
WebPRescalerExportRow(p->scaler_u);
|
||||||
WebPRescalerExportRow(&p->scaler_v, 0);
|
WebPRescalerExportRow(p->scaler_v);
|
||||||
convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
|
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst,
|
||||||
dst, p->scaler_y.dst_width);
|
dst, p->scaler_y->dst_width);
|
||||||
dst += buf->stride;
|
dst += buf->stride;
|
||||||
++num_lines_out;
|
++num_lines_out;
|
||||||
}
|
}
|
||||||
@@ -393,57 +381,56 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
while (j < mb_h) {
|
while (j < mb_h) {
|
||||||
const int y_lines_in =
|
const int y_lines_in =
|
||||||
WebPRescalerImport(&p->scaler_y, mb_h - j,
|
WebPRescalerImport(p->scaler_y, mb_h - j,
|
||||||
io->y + j * io->y_stride, io->y_stride);
|
io->y + 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 =
|
const int u_lines_in =
|
||||||
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
|
WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j,
|
||||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||||
const int v_lines_in =
|
const int v_lines_in =
|
||||||
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
|
WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j,
|
||||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||||
(void)v_lines_in; // remove a gcc warning
|
(void)v_lines_in; // remove a gcc warning
|
||||||
assert(u_lines_in == v_lines_in);
|
assert(u_lines_in == v_lines_in);
|
||||||
j += y_lines_in;
|
|
||||||
uv_j += u_lines_in;
|
uv_j += u_lines_in;
|
||||||
num_lines_out += ExportRGB(p, num_lines_out);
|
}
|
||||||
|
num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
|
||||||
}
|
}
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
|
||||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||||
const int alpha_first =
|
const int alpha_first =
|
||||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||||
uint32_t alpha_mask = 0xff;
|
uint32_t non_opaque = 0;
|
||||||
const int width = p->scaler_a.dst_width;
|
const int width = p->scaler_a->dst_width;
|
||||||
|
|
||||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||||
int i;
|
num_lines_out < max_lines_out) {
|
||||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
assert(y_pos + num_lines_out < p->output->height);
|
||||||
WebPRescalerExportRow(&p->scaler_a, 0);
|
WebPRescalerExportRow(p->scaler_a);
|
||||||
for (i = 0; i < width; ++i) {
|
non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0);
|
||||||
const uint32_t alpha_value = p->scaler_a.dst[i];
|
|
||||||
dst[4 * i] = alpha_value;
|
|
||||||
alpha_mask &= alpha_value;
|
|
||||||
}
|
|
||||||
dst += buf->stride;
|
dst += buf->stride;
|
||||||
++num_lines_out;
|
++num_lines_out;
|
||||||
}
|
}
|
||||||
if (is_premult_alpha && alpha_mask != 0xff) {
|
if (is_premult_alpha && non_opaque) {
|
||||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||||
width, num_lines_out, buf->stride);
|
width, num_lines_out, buf->stride);
|
||||||
}
|
}
|
||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
|
||||||
|
int max_lines_out) {
|
||||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
|
||||||
#ifdef WEBP_SWAP_16BIT_CSP
|
#ifdef WEBP_SWAP_16BIT_CSP
|
||||||
uint8_t* alpha_dst = base_rgba;
|
uint8_t* alpha_dst = base_rgba;
|
||||||
#else
|
#else
|
||||||
@@ -451,17 +438,18 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
|||||||
#endif
|
#endif
|
||||||
int num_lines_out = 0;
|
int num_lines_out = 0;
|
||||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||||
const int width = p->scaler_a.dst_width;
|
const int width = p->scaler_a->dst_width;
|
||||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||||
uint32_t alpha_mask = 0x0f;
|
uint32_t alpha_mask = 0x0f;
|
||||||
|
|
||||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||||
|
num_lines_out < max_lines_out) {
|
||||||
int i;
|
int i;
|
||||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
assert(y_pos + num_lines_out < p->output->height);
|
||||||
WebPRescalerExportRow(&p->scaler_a, 0);
|
WebPRescalerExportRow(p->scaler_a);
|
||||||
for (i = 0; i < width; ++i) {
|
for (i = 0; i < width; ++i) {
|
||||||
// Fill in the alpha value (converted to 4 bits).
|
// Fill in the alpha value (converted to 4 bits).
|
||||||
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
|
const uint32_t alpha_value = p->scaler_a->dst[i] >> 4;
|
||||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||||
alpha_mask &= alpha_value;
|
alpha_mask &= alpha_value;
|
||||||
}
|
}
|
||||||
@@ -474,15 +462,17 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
|||||||
return num_lines_out;
|
return num_lines_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||||
|
int expected_num_out_lines) {
|
||||||
if (io->a != NULL) {
|
if (io->a != NULL) {
|
||||||
WebPRescaler* const scaler = &p->scaler_a;
|
WebPRescaler* const scaler = p->scaler_a;
|
||||||
int j = 0;
|
int lines_left = expected_num_out_lines;
|
||||||
int pos = 0;
|
const int y_end = p->last_y + lines_left;
|
||||||
while (j < io->mb_h) {
|
while (lines_left > 0) {
|
||||||
j += WebPRescalerImport(scaler, io->mb_h - j,
|
const int row_offset = scaler->src_y - io->mb_y;
|
||||||
io->a + j * io->width, io->width);
|
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
|
||||||
pos += p->emit_alpha_row(p, pos);
|
io->a + row_offset * io->width, io->width);
|
||||||
|
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -495,9 +485,11 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
const int uv_in_width = (io->mb_w + 1) >> 1;
|
const int uv_in_width = (io->mb_w + 1) >> 1;
|
||||||
const int uv_in_height = (io->mb_h + 1) >> 1;
|
const int uv_in_height = (io->mb_h + 1) >> 1;
|
||||||
const size_t work_size = 2 * out_width; // scratch memory for one rescaler
|
const size_t work_size = 2 * out_width; // scratch memory for one rescaler
|
||||||
int32_t* work; // rescalers work area
|
rescaler_t* work; // rescalers work area
|
||||||
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
||||||
size_t tmp_size1, tmp_size2, total_size;
|
size_t tmp_size1, tmp_size2, total_size, rescaler_size;
|
||||||
|
WebPRescaler* scalers;
|
||||||
|
const int num_rescalers = has_alpha ? 4 : 3;
|
||||||
|
|
||||||
tmp_size1 = 3 * work_size;
|
tmp_size1 = 3 * work_size;
|
||||||
tmp_size2 = 3 * out_width;
|
tmp_size2 = 3 * out_width;
|
||||||
@@ -506,30 +498,36 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
|||||||
tmp_size2 += out_width;
|
tmp_size2 += out_width;
|
||||||
}
|
}
|
||||||
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
|
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
|
||||||
p->memory = WebPSafeCalloc(1ULL, total_size);
|
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||||
|
|
||||||
|
p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size);
|
||||||
if (p->memory == NULL) {
|
if (p->memory == NULL) {
|
||||||
return 0; // memory error
|
return 0; // memory error
|
||||||
}
|
}
|
||||||
work = (int32_t*)p->memory;
|
work = (rescaler_t*)p->memory;
|
||||||
tmp = (uint8_t*)(work + tmp_size1);
|
tmp = (uint8_t*)(work + tmp_size1);
|
||||||
WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
|
|
||||||
|
scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size);
|
||||||
|
p->scaler_y = &scalers[0];
|
||||||
|
p->scaler_u = &scalers[1];
|
||||||
|
p->scaler_v = &scalers[2];
|
||||||
|
p->scaler_a = has_alpha ? &scalers[3] : NULL;
|
||||||
|
|
||||||
|
WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
|
||||||
tmp + 0 * out_width, out_width, out_height, 0, 1,
|
tmp + 0 * out_width, out_width, out_height, 0, 1,
|
||||||
io->mb_w, out_width, io->mb_h, out_height,
|
|
||||||
work + 0 * work_size);
|
work + 0 * work_size);
|
||||||
WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
|
WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
|
||||||
tmp + 1 * out_width, out_width, out_height, 0, 1,
|
tmp + 1 * out_width, out_width, out_height, 0, 1,
|
||||||
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
|
|
||||||
work + 1 * work_size);
|
work + 1 * work_size);
|
||||||
WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
|
WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
|
||||||
tmp + 2 * out_width, out_width, out_height, 0, 1,
|
tmp + 2 * out_width, out_width, out_height, 0, 1,
|
||||||
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
|
|
||||||
work + 2 * work_size);
|
work + 2 * work_size);
|
||||||
p->emit = EmitRescaledRGB;
|
p->emit = EmitRescaledRGB;
|
||||||
|
WebPInitYUV444Converters();
|
||||||
|
|
||||||
if (has_alpha) {
|
if (has_alpha) {
|
||||||
WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
|
WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
|
||||||
tmp + 3 * out_width, out_width, out_height, 0, 1,
|
tmp + 3 * out_width, out_width, out_height, 0, 1,
|
||||||
io->mb_w, out_width, io->mb_h, out_height,
|
|
||||||
work + 3 * work_size);
|
work + 3 * work_size);
|
||||||
p->emit_alpha = EmitRescaledAlphaRGB;
|
p->emit_alpha = EmitRescaledAlphaRGB;
|
||||||
if (p->output->colorspace == MODE_RGBA_4444 ||
|
if (p->output->colorspace == MODE_RGBA_4444 ||
|
||||||
@@ -569,6 +567,7 @@ static int CustomSetup(VP8Io* io) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (is_rgb) {
|
if (is_rgb) {
|
||||||
|
WebPInitSamplers();
|
||||||
p->emit = EmitSampledRGB; // default
|
p->emit = EmitSampledRGB; // default
|
||||||
if (io->fancy_upsampling) {
|
if (io->fancy_upsampling) {
|
||||||
#ifdef FANCY_UPSAMPLING
|
#ifdef FANCY_UPSAMPLING
|
||||||
@@ -583,8 +582,6 @@ static int CustomSetup(VP8Io* io) {
|
|||||||
p->emit = EmitFancyRGB;
|
p->emit = EmitFancyRGB;
|
||||||
WebPInitUpsamplers();
|
WebPInitUpsamplers();
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
WebPInitSamplers();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p->emit = EmitYUV;
|
p->emit = EmitYUV;
|
||||||
@@ -621,7 +618,7 @@ static int CustomPut(const VP8Io* io) {
|
|||||||
}
|
}
|
||||||
num_lines_out = p->emit(io, p);
|
num_lines_out = p->emit(io, p);
|
||||||
if (p->emit_alpha != NULL) {
|
if (p->emit_alpha != NULL) {
|
||||||
p->emit_alpha(io, p);
|
p->emit_alpha(io, p, num_lines_out);
|
||||||
}
|
}
|
||||||
p->last_y += num_lines_out;
|
p->last_y += num_lines_out;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
//
|
//
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
|
|
||||||
static WEBP_INLINE int clip(int v, int M) {
|
static WEBP_INLINE int clip(int v, int M) {
|
||||||
return v < 0 ? 0 : v > M ? M : v;
|
return v < 0 ? 0 : v > M ? M : v;
|
||||||
@@ -11,10 +11,13 @@
|
|||||||
//
|
//
|
||||||
// Author: Skal (pascal.massimino@gmail.com)
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
#include "vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "../utils/bit_reader_inl.h"
|
#include "../utils/bit_reader_inl_utils.h"
|
||||||
|
|
||||||
|
#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
|
||||||
|
// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
|
||||||
#define USE_GENERIC_TREE
|
#define USE_GENERIC_TREE
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_GENERIC_TREE
|
#ifdef USE_GENERIC_TREE
|
||||||
static const int8_t kYModesIntra4[18] = {
|
static const int8_t kYModesIntra4[18] = {
|
||||||
@@ -494,6 +497,12 @@ static const uint8_t
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Paragraph 9.9
|
// Paragraph 9.9
|
||||||
|
|
||||||
|
static const int 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) {
|
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||||
VP8Proba* const proba = &dec->proba_;
|
VP8Proba* const proba = &dec->proba_;
|
||||||
int t, b, c, p;
|
int t, b, c, p;
|
||||||
@@ -507,6 +516,9 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (b = 0; b < 16 + 1; ++b) {
|
||||||
|
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dec->use_skip_proba_ = VP8Get(br);
|
dec->use_skip_proba_ = VP8Get(br);
|
||||||
if (dec->use_skip_proba_) {
|
if (dec->use_skip_proba_) {
|
||||||
@@ -13,11 +13,11 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./alphai.h"
|
#include "./alphai_dec.h"
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "./vp8li.h"
|
#include "./vp8li_dec.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "../utils/bit_reader_inl.h"
|
#include "../utils/bit_reader_inl_utils.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -26,6 +26,16 @@ int WebPGetDecoderVersion(void) {
|
|||||||
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
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
|
// VP8Decoder
|
||||||
|
|
||||||
@@ -50,7 +60,8 @@ VP8Decoder* VP8New(void) {
|
|||||||
SetOk(dec);
|
SetOk(dec);
|
||||||
WebPGetWorkerInterface()->Init(&dec->worker_);
|
WebPGetWorkerInterface()->Init(&dec->worker_);
|
||||||
dec->ready_ = 0;
|
dec->ready_ = 0;
|
||||||
dec->num_parts_ = 1;
|
dec->num_parts_minus_one_ = 0;
|
||||||
|
InitGetCoeffs();
|
||||||
}
|
}
|
||||||
return dec;
|
return dec;
|
||||||
}
|
}
|
||||||
@@ -75,10 +86,7 @@ void VP8Delete(VP8Decoder* const dec) {
|
|||||||
|
|
||||||
int VP8SetError(VP8Decoder* const dec,
|
int VP8SetError(VP8Decoder* const dec,
|
||||||
VP8StatusCode error, const char* const msg) {
|
VP8StatusCode error, const char* const msg) {
|
||||||
// TODO This check would be unnecessary if alpha decompression was separated
|
// The oldest error reported takes precedence over the new one.
|
||||||
// from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
|
|
||||||
// something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
|
|
||||||
// failure.
|
|
||||||
if (dec->status_ == VP8_STATUS_OK) {
|
if (dec->status_ == VP8_STATUS_OK) {
|
||||||
dec->status_ = error;
|
dec->status_ = error;
|
||||||
dec->error_msg_ = msg;
|
dec->error_msg_ = msg;
|
||||||
@@ -193,25 +201,27 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
|||||||
const uint8_t* sz = buf;
|
const uint8_t* sz = buf;
|
||||||
const uint8_t* buf_end = buf + size;
|
const uint8_t* buf_end = buf + size;
|
||||||
const uint8_t* part_start;
|
const uint8_t* part_start;
|
||||||
int last_part;
|
size_t size_left = size;
|
||||||
int p;
|
size_t last_part;
|
||||||
|
size_t p;
|
||||||
|
|
||||||
dec->num_parts_ = 1 << VP8GetValue(br, 2);
|
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1;
|
||||||
last_part = dec->num_parts_ - 1;
|
last_part = dec->num_parts_minus_one_;
|
||||||
part_start = buf + last_part * 3;
|
if (size < 3 * last_part) {
|
||||||
if (buf_end < part_start) {
|
|
||||||
// we can't even read the sizes with sz[]! That's a failure.
|
// we can't even read the sizes with sz[]! That's a failure.
|
||||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||||
}
|
}
|
||||||
|
part_start = buf + last_part * 3;
|
||||||
|
size_left -= last_part * 3;
|
||||||
for (p = 0; p < last_part; ++p) {
|
for (p = 0; p < last_part; ++p) {
|
||||||
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||||
const uint8_t* part_end = part_start + psize;
|
if (psize > size_left) psize = size_left;
|
||||||
if (part_end > buf_end) part_end = buf_end;
|
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
||||||
VP8InitBitReader(dec->parts_ + p, part_start, part_end);
|
part_start += psize;
|
||||||
part_start = part_end;
|
size_left -= psize;
|
||||||
sz += 3;
|
sz += 3;
|
||||||
}
|
}
|
||||||
VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
|
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||||
}
|
}
|
||||||
@@ -274,12 +284,14 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
frm_hdr->profile_ = (bits >> 1) & 7;
|
frm_hdr->profile_ = (bits >> 1) & 7;
|
||||||
frm_hdr->show_ = (bits >> 4) & 1;
|
frm_hdr->show_ = (bits >> 4) & 1;
|
||||||
frm_hdr->partition_length_ = (bits >> 5);
|
frm_hdr->partition_length_ = (bits >> 5);
|
||||||
if (frm_hdr->profile_ > 3)
|
if (frm_hdr->profile_ > 3) {
|
||||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||||
"Incorrect keyframe parameters.");
|
"Incorrect keyframe parameters.");
|
||||||
if (!frm_hdr->show_)
|
}
|
||||||
|
if (!frm_hdr->show_) {
|
||||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||||
"Frame not displayable.");
|
"Frame not displayable.");
|
||||||
|
}
|
||||||
buf += 3;
|
buf += 3;
|
||||||
buf_size -= 3;
|
buf_size -= 3;
|
||||||
}
|
}
|
||||||
@@ -304,15 +316,22 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
|
|
||||||
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
||||||
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
||||||
|
|
||||||
// Setup default output area (can be later modified during io->setup())
|
// Setup default output area (can be later modified during io->setup())
|
||||||
io->width = pic_hdr->width_;
|
io->width = pic_hdr->width_;
|
||||||
io->height = pic_hdr->height_;
|
io->height = pic_hdr->height_;
|
||||||
io->use_scaling = 0;
|
// 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->use_cropping = 0;
|
||||||
io->crop_top = 0;
|
io->crop_top = 0;
|
||||||
io->crop_left = 0;
|
io->crop_left = 0;
|
||||||
io->crop_right = io->width;
|
io->crop_right = io->width;
|
||||||
io->crop_bottom = io->height;
|
io->crop_bottom = io->height;
|
||||||
|
io->use_scaling = 0;
|
||||||
|
io->scaled_width = io->width;
|
||||||
|
io->scaled_height = io->height;
|
||||||
|
|
||||||
io->mb_w = io->width; // sanity check
|
io->mb_w = io->width; // sanity check
|
||||||
io->mb_h = io->height; // ditto
|
io->mb_h = io->height; // ditto
|
||||||
|
|
||||||
@@ -328,7 +347,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
br = &dec->br_;
|
br = &dec->br_;
|
||||||
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
|
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
||||||
buf += frm_hdr->partition_length_;
|
buf += frm_hdr->partition_length_;
|
||||||
buf_size -= frm_hdr->partition_length_;
|
buf_size -= frm_hdr->partition_length_;
|
||||||
|
|
||||||
@@ -371,11 +390,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Residual decoding (Paragraph 13.2 / 13.3)
|
// Residual decoding (Paragraph 13.2 / 13.3)
|
||||||
|
|
||||||
static const int kBands[16 + 1] = {
|
|
||||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
0 // extra entry as sentinel
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
||||||
static const uint8_t kCat4[] = { 176, 155, 140, 135, 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 kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
||||||
@@ -419,20 +433,20 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the position of the last non-zero coeff plus one
|
// Returns the position of the last non-zero coeff plus one
|
||||||
static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob,
|
static int GetCoeffsFast(VP8BitReader* const br,
|
||||||
|
const VP8BandProbas* const prob[],
|
||||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||||
// n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
|
const uint8_t* p = prob[n]->probas_[ctx];
|
||||||
const uint8_t* p = prob[n].probas_[ctx];
|
|
||||||
for (; n < 16; ++n) {
|
for (; n < 16; ++n) {
|
||||||
if (!VP8GetBit(br, p[0])) {
|
if (!VP8GetBit(br, p[0])) {
|
||||||
return n; // previous coeff was last non-zero coeff
|
return n; // previous coeff was last non-zero coeff
|
||||||
}
|
}
|
||||||
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
|
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
|
||||||
p = prob[kBands[++n]].probas_[0];
|
p = prob[++n]->probas_[0];
|
||||||
if (n == 16) return 16;
|
if (n == 16) return 16;
|
||||||
}
|
}
|
||||||
{ // non zero coeff
|
{ // non zero coeff
|
||||||
const VP8ProbaArray* const p_ctx = &prob[kBands[n + 1]].probas_[0];
|
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||||
int v;
|
int v;
|
||||||
if (!VP8GetBit(br, p[2])) {
|
if (!VP8GetBit(br, p[2])) {
|
||||||
v = 1;
|
v = 1;
|
||||||
@@ -447,6 +461,46 @@ static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob,
|
|||||||
return 16;
|
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])) {
|
||||||
|
return n; // previous coeff was last non-zero coeff
|
||||||
|
}
|
||||||
|
while (!VP8GetBitAlt(br, p[1])) { // 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])) {
|
||||||
|
v = 1;
|
||||||
|
p = p_ctx[1];
|
||||||
|
} else {
|
||||||
|
v = GetLargeValue(br, p);
|
||||||
|
p = p_ctx[2];
|
||||||
|
}
|
||||||
|
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) {
|
||||||
|
if (GetCoeffs == NULL) {
|
||||||
|
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) {
|
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
||||||
nz_coeffs <<= 2;
|
nz_coeffs <<= 2;
|
||||||
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
||||||
@@ -455,8 +509,8 @@ static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
|||||||
|
|
||||||
static int ParseResiduals(VP8Decoder* const dec,
|
static int ParseResiduals(VP8Decoder* const dec,
|
||||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||||
VP8BandProbas (* const bands)[NUM_BANDS] = dec->proba_.bands_;
|
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
||||||
const VP8BandProbas* ac_proba;
|
const VP8BandProbas* const * ac_proba;
|
||||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||||
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
||||||
int16_t* dst = block->coeffs_;
|
int16_t* dst = block->coeffs_;
|
||||||
@@ -562,6 +616,7 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
|||||||
}
|
}
|
||||||
block->non_zero_y_ = 0;
|
block->non_zero_y_ = 0;
|
||||||
block->non_zero_uv_ = 0;
|
block->non_zero_uv_ = 0;
|
||||||
|
block->dither_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->filter_type_ > 0) { // store filter info
|
if (dec->filter_type_ > 0) { // store filter info
|
||||||
@@ -585,7 +640,7 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
|||||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||||
// Parse bitstream for this row.
|
// Parse bitstream for this row.
|
||||||
VP8BitReader* const token_br =
|
VP8BitReader* const token_br =
|
||||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||||
"Premature end-of-partition0 encountered.");
|
"Premature end-of-partition0 encountered.");
|
||||||
@@ -655,8 +710,7 @@ void VP8Clear(VP8Decoder* const dec) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WebPGetWorkerInterface()->End(&dec->worker_);
|
WebPGetWorkerInterface()->End(&dec->worker_);
|
||||||
ALPHDelete(dec->alph_dec_);
|
WebPDeallocateAlphaMemory(dec);
|
||||||
dec->alph_dec_ = NULL;
|
|
||||||
WebPSafeFree(dec->mem_);
|
WebPSafeFree(dec->mem_);
|
||||||
dec->mem_ = NULL;
|
dec->mem_ = NULL;
|
||||||
dec->mem_size_ = 0;
|
dec->mem_size_ = 0;
|
||||||
@@ -665,4 +719,3 @@ void VP8Clear(VP8Decoder* const dec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -15,10 +15,11 @@
|
|||||||
#define WEBP_DEC_VP8I_H_
|
#define WEBP_DEC_VP8I_H_
|
||||||
|
|
||||||
#include <string.h> // for memcpy()
|
#include <string.h> // for memcpy()
|
||||||
#include "./vp8li.h"
|
#include "./common_dec.h"
|
||||||
#include "../utils/bit_reader.h"
|
#include "./vp8li_dec.h"
|
||||||
#include "../utils/random.h"
|
#include "../utils/bit_reader_utils.h"
|
||||||
#include "../utils/thread.h"
|
#include "../utils/random_utils.h"
|
||||||
|
#include "../utils/thread_utils.h"
|
||||||
#include "../dsp/dsp.h"
|
#include "../dsp/dsp.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -30,46 +31,10 @@ extern "C" {
|
|||||||
|
|
||||||
// version numbers
|
// version numbers
|
||||||
#define DEC_MAJ_VERSION 0
|
#define DEC_MAJ_VERSION 0
|
||||||
#define DEC_MIN_VERSION 4
|
#define DEC_MIN_VERSION 6
|
||||||
#define DEC_REV_VERSION 1
|
#define DEC_REV_VERSION 0
|
||||||
|
|
||||||
// intra prediction modes
|
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||||
enum { B_DC_PRED = 0, // 4x4 modes
|
|
||||||
B_TM_PRED,
|
|
||||||
B_VE_PRED,
|
|
||||||
B_HE_PRED,
|
|
||||||
B_RD_PRED,
|
|
||||||
B_VR_PRED,
|
|
||||||
B_LD_PRED,
|
|
||||||
B_VL_PRED,
|
|
||||||
B_HD_PRED,
|
|
||||||
B_HU_PRED,
|
|
||||||
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
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
NUM_BANDS = 8,
|
|
||||||
NUM_CTX = 3,
|
|
||||||
NUM_PROBAS = 11,
|
|
||||||
NUM_MV_PROBAS = 19 };
|
|
||||||
|
|
||||||
// YUV-cache parameters.
|
|
||||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||||
// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
|
// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
|
||||||
// in order to be SIMD-friendly. We also need to store the top, left and
|
// in order to be SIMD-friendly. We also need to store the top, left and
|
||||||
@@ -91,8 +56,6 @@ enum { MB_FEATURE_TREE_PROBS = 3,
|
|||||||
// 'y' = y-samples 'u' = u-samples 'v' = u-samples
|
// 'y' = y-samples 'u' = u-samples 'v' = u-samples
|
||||||
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
||||||
// 't' = extra top-right sample for 4x4 modes
|
// 't' = extra top-right sample for 4x4 modes
|
||||||
// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size.
|
|
||||||
#define BPS 32 // this is the common stride used by yuv[]
|
|
||||||
#define YUV_SIZE (BPS * 17 + BPS * 9)
|
#define YUV_SIZE (BPS * 17 + BPS * 9)
|
||||||
#define Y_SIZE (BPS * 17)
|
#define Y_SIZE (BPS * 17)
|
||||||
#define Y_OFF (BPS * 1 + 8)
|
#define Y_OFF (BPS * 1 + 8)
|
||||||
@@ -130,7 +93,6 @@ typedef struct {
|
|||||||
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||||
} VP8SegmentHeader;
|
} VP8SegmentHeader;
|
||||||
|
|
||||||
|
|
||||||
// probas associated to one of the contexts
|
// probas associated to one of the contexts
|
||||||
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
|
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
|
||||||
|
|
||||||
@@ -143,6 +105,7 @@ typedef struct {
|
|||||||
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
||||||
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
||||||
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
|
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
|
||||||
|
const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1];
|
||||||
} VP8Proba;
|
} VP8Proba;
|
||||||
|
|
||||||
// Filter parameters
|
// Filter parameters
|
||||||
@@ -246,8 +209,8 @@ struct VP8Decoder {
|
|||||||
int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
|
int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
|
||||||
int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
|
int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
|
||||||
|
|
||||||
// number of partitions.
|
// number of partitions minus one.
|
||||||
int num_parts_;
|
uint32_t num_parts_minus_one_;
|
||||||
// per-partition boolean decoders.
|
// per-partition boolean decoders.
|
||||||
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
||||||
|
|
||||||
@@ -296,8 +259,10 @@ struct VP8Decoder {
|
|||||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||||
size_t alpha_data_size_;
|
size_t alpha_data_size_;
|
||||||
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
|
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
|
||||||
|
uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_
|
||||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||||
int alpha_dithering_; // derived from decoding options (0=off, 100=full).
|
const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL)
|
||||||
|
int alpha_dithering_; // derived from decoding options (0=off, 100=full)
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -317,7 +282,7 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
|
|||||||
void VP8ParseQuant(VP8Decoder* const dec);
|
void VP8ParseQuant(VP8Decoder* const dec);
|
||||||
|
|
||||||
// in frame.c
|
// in frame.c
|
||||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
|
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
||||||
// Call io->setup() and finish setting up scan parameters.
|
// Call io->setup() and finish setting up scan parameters.
|
||||||
// After this call returns, one must always call VP8ExitCritical() with the
|
// After this call returns, one must always call VP8ExitCritical() with the
|
||||||
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
||||||
@@ -343,6 +308,7 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
|
|||||||
|
|
||||||
// in alpha.c
|
// in alpha.c
|
||||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||||
|
const VP8Io* const io,
|
||||||
int row, int num_rows);
|
int row, int num_rows);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,10 @@
|
|||||||
#define WEBP_DEC_VP8LI_H_
|
#define WEBP_DEC_VP8LI_H_
|
||||||
|
|
||||||
#include <string.h> // for memcpy()
|
#include <string.h> // for memcpy()
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
#include "../utils/bit_reader.h"
|
#include "../utils/bit_reader_utils.h"
|
||||||
#include "../utils/color_cache.h"
|
#include "../utils/color_cache_utils.h"
|
||||||
#include "../utils/huffman.h"
|
#include "../utils/huffman_utils.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -43,6 +43,7 @@ struct VP8LTransform {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int color_cache_size_;
|
int color_cache_size_;
|
||||||
VP8LColorCache color_cache_;
|
VP8LColorCache color_cache_;
|
||||||
|
VP8LColorCache saved_color_cache_; // for incremental
|
||||||
|
|
||||||
int huffman_mask_;
|
int huffman_mask_;
|
||||||
int huffman_subsample_bits_;
|
int huffman_subsample_bits_;
|
||||||
@@ -50,12 +51,12 @@ typedef struct {
|
|||||||
uint32_t *huffman_image_;
|
uint32_t *huffman_image_;
|
||||||
int num_htree_groups_;
|
int num_htree_groups_;
|
||||||
HTreeGroup *htree_groups_;
|
HTreeGroup *htree_groups_;
|
||||||
|
HuffmanCode *huffman_tables_;
|
||||||
} VP8LMetadata;
|
} VP8LMetadata;
|
||||||
|
|
||||||
typedef struct VP8LDecoder VP8LDecoder;
|
typedef struct VP8LDecoder VP8LDecoder;
|
||||||
struct VP8LDecoder {
|
struct VP8LDecoder {
|
||||||
VP8StatusCode status_;
|
VP8StatusCode status_;
|
||||||
VP8LDecodeState action_;
|
|
||||||
VP8LDecodeState state_;
|
VP8LDecodeState state_;
|
||||||
VP8Io *io_;
|
VP8Io *io_;
|
||||||
|
|
||||||
@@ -66,6 +67,9 @@ struct VP8LDecoder {
|
|||||||
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||||
|
|
||||||
VP8LBitReader br_;
|
VP8LBitReader br_;
|
||||||
|
int incremental_; // if true, incremental decoding is expected
|
||||||
|
VP8LBitReader saved_br_; // note: could be local variables too
|
||||||
|
int saved_last_pixel_;
|
||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
@@ -96,8 +100,7 @@ struct ALPHDecoder; // Defined in dec/alphai.h.
|
|||||||
// Decodes image header for alpha data stored using lossless compression.
|
// Decodes image header for alpha data stored using lossless compression.
|
||||||
// Returns false in case of error.
|
// Returns false in case of error.
|
||||||
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||||
const uint8_t* const data, size_t data_size,
|
const uint8_t* const data, size_t data_size);
|
||||||
uint8_t* const output);
|
|
||||||
|
|
||||||
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
|
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
|
||||||
// already decoded in previous call(s), it will resume decoding from where it
|
// already decoded in previous call(s), it will resume decoding from where it
|
||||||
@@ -13,9 +13,10 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./vp8i.h"
|
#include "./vp8i_dec.h"
|
||||||
#include "./vp8li.h"
|
#include "./vp8li_dec.h"
|
||||||
#include "./webpi.h"
|
#include "./webpi_dec.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
#include "../webp/mux_types.h" // ALPHA_FLAG
|
#include "../webp/mux_types.h" // ALPHA_FLAG
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -38,19 +39,11 @@
|
|||||||
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
|
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
|
||||||
// 24..26 Width of the Canvas Image.
|
// 24..26 Width of the Canvas Image.
|
||||||
// 27..29 Height of the Canvas Image.
|
// 27..29 Height of the Canvas Image.
|
||||||
// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
|
// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L,
|
||||||
// VP8L, XMP, EXIF ...)
|
// XMP, EXIF ...)
|
||||||
// All sizes are in little-endian order.
|
// All sizes are in little-endian order.
|
||||||
// Note: chunk data size must be padded to multiple of 2 when written.
|
// Note: chunk data size must be padded to multiple of 2 when written.
|
||||||
|
|
||||||
static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
|
|
||||||
return data[0] | (data[1] << 8) | (data[2] << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
|
|
||||||
return (uint32_t)get_le24(data) | (data[3] << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validates the RIFF container (if detected) and skips over it.
|
// Validates the RIFF container (if detected) and skips over it.
|
||||||
// If a RIFF container is detected, returns:
|
// If a RIFF container is detected, returns:
|
||||||
// VP8_STATUS_BITSTREAM_ERROR for invalid header,
|
// VP8_STATUS_BITSTREAM_ERROR for invalid header,
|
||||||
@@ -70,7 +63,7 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
|||||||
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
|
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
|
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
|
||||||
} else {
|
} else {
|
||||||
const uint32_t size = get_le32(*data + TAG_SIZE);
|
const uint32_t size = GetLE32(*data + TAG_SIZE);
|
||||||
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
|
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
|
||||||
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
|
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR;
|
return VP8_STATUS_BITSTREAM_ERROR;
|
||||||
@@ -116,7 +109,7 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
|||||||
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
|
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
|
||||||
int width, height;
|
int width, height;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
|
const uint32_t chunk_size = GetLE32(*data + TAG_SIZE);
|
||||||
if (chunk_size != VP8X_CHUNK_SIZE) {
|
if (chunk_size != VP8X_CHUNK_SIZE) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
|
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
|
||||||
}
|
}
|
||||||
@@ -125,9 +118,9 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
|||||||
if (*data_size < vp8x_size) {
|
if (*data_size < vp8x_size) {
|
||||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||||
}
|
}
|
||||||
flags = get_le32(*data + 8);
|
flags = GetLE32(*data + 8);
|
||||||
width = 1 + get_le24(*data + 12);
|
width = 1 + GetLE24(*data + 12);
|
||||||
height = 1 + get_le24(*data + 15);
|
height = 1 + GetLE24(*data + 15);
|
||||||
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
|
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
|
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
|
||||||
}
|
}
|
||||||
@@ -181,7 +174,7 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
|||||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_size = get_le32(buf + TAG_SIZE);
|
chunk_size = GetLE32(buf + TAG_SIZE);
|
||||||
if (chunk_size > MAX_CHUNK_PAYLOAD) {
|
if (chunk_size > MAX_CHUNK_PAYLOAD) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||||
}
|
}
|
||||||
@@ -247,7 +240,7 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
|||||||
|
|
||||||
if (is_vp8 || is_vp8l) {
|
if (is_vp8 || is_vp8l) {
|
||||||
// Bitstream contains VP8/VP8L header.
|
// Bitstream contains VP8/VP8L header.
|
||||||
const uint32_t size = get_le32(data + TAG_SIZE);
|
const uint32_t size = GetLE32(data + TAG_SIZE);
|
||||||
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
|
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
|
||||||
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
|
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
|
||||||
}
|
}
|
||||||
@@ -296,7 +289,6 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
|||||||
int found_riff = 0;
|
int found_riff = 0;
|
||||||
int found_vp8x = 0;
|
int found_vp8x = 0;
|
||||||
int animation_present = 0;
|
int animation_present = 0;
|
||||||
int fragments_present = 0;
|
|
||||||
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
|
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
|
||||||
|
|
||||||
VP8StatusCode status;
|
VP8StatusCode status;
|
||||||
@@ -325,7 +317,6 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
|||||||
return status; // Wrong VP8X / insufficient data.
|
return status; // Wrong VP8X / insufficient data.
|
||||||
}
|
}
|
||||||
animation_present = !!(flags & ANIMATION_FLAG);
|
animation_present = !!(flags & ANIMATION_FLAG);
|
||||||
fragments_present = !!(flags & FRAGMENTS_FLAG);
|
|
||||||
if (!found_riff && found_vp8x) {
|
if (!found_riff && found_vp8x) {
|
||||||
// Note: This restriction may be removed in the future, if it becomes
|
// Note: This restriction may be removed in the future, if it becomes
|
||||||
// necessary to send VP8X chunk to the decoder.
|
// necessary to send VP8X chunk to the decoder.
|
||||||
@@ -337,8 +328,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
|||||||
|
|
||||||
image_width = canvas_width;
|
image_width = canvas_width;
|
||||||
image_height = canvas_height;
|
image_height = canvas_height;
|
||||||
if (found_vp8x && (animation_present || fragments_present) &&
|
if (found_vp8x && animation_present && headers == NULL) {
|
||||||
headers == NULL) {
|
|
||||||
status = VP8_STATUS_OK;
|
status = VP8_STATUS_OK;
|
||||||
goto ReturnWidthHeight; // Just return features from VP8X header.
|
goto ReturnWidthHeight; // Just return features from VP8X header.
|
||||||
}
|
}
|
||||||
@@ -369,7 +359,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
|||||||
return VP8_STATUS_BITSTREAM_ERROR;
|
return VP8_STATUS_BITSTREAM_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format != NULL && !(animation_present || fragments_present)) {
|
if (format != NULL && !animation_present) {
|
||||||
*format = hdrs.is_lossless ? 2 : 1;
|
*format = hdrs.is_lossless ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +412,8 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||||
VP8StatusCode status;
|
// status is marked volatile as a workaround for a clang-3.8 (aarch64) bug
|
||||||
|
volatile VP8StatusCode status;
|
||||||
int has_animation = 0;
|
int has_animation = 0;
|
||||||
assert(headers != NULL);
|
assert(headers != NULL);
|
||||||
// fill out headers, ignore width/height/has_alpha.
|
// fill out headers, ignore width/height/has_alpha.
|
||||||
@@ -519,13 +510,13 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
|||||||
|
|
||||||
if (status != VP8_STATUS_OK) {
|
if (status != VP8_STATUS_OK) {
|
||||||
WebPFreeDecBuffer(params->output);
|
WebPFreeDecBuffer(params->output);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
#if WEBP_DECODER_ABI_VERSION > 0x0203
|
|
||||||
if (params->options != NULL && params->options->flip) {
|
if (params->options != NULL && params->options->flip) {
|
||||||
|
// This restores the original stride values if options->flip was used
|
||||||
|
// during the call to WebPAllocateDecBuffer above.
|
||||||
status = WebPFlipBuffer(params->output);
|
status = WebPFlipBuffer(params->output);
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,9 +758,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebPResetDecParams(¶ms);
|
WebPResetDecParams(¶ms);
|
||||||
params.output = &config->output;
|
|
||||||
params.options = &config->options;
|
params.options = &config->options;
|
||||||
|
params.output = &config->output;
|
||||||
|
if (WebPAvoidSlowMemory(params.output, &config->input)) {
|
||||||
|
// decoding to slow memory: use a temporary in-mem buffer to decode into.
|
||||||
|
WebPDecBuffer in_mem_buffer;
|
||||||
|
WebPInitDecBuffer(&in_mem_buffer);
|
||||||
|
in_mem_buffer.colorspace = config->output.colorspace;
|
||||||
|
in_mem_buffer.width = config->input.width;
|
||||||
|
in_mem_buffer.height = config->input.height;
|
||||||
|
params.output = &in_mem_buffer;
|
||||||
status = DecodeInto(data, data_size, ¶ms);
|
status = DecodeInto(data, data_size, ¶ms);
|
||||||
|
if (status == VP8_STATUS_OK) { // do the slow-copy
|
||||||
|
status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output);
|
||||||
|
}
|
||||||
|
WebPFreeDecBuffer(&in_mem_buffer);
|
||||||
|
} else {
|
||||||
|
status = DecodeInto(data, data_size, ¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -808,15 +814,17 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
|||||||
// Scaling
|
// Scaling
|
||||||
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
|
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
|
||||||
if (io->use_scaling) {
|
if (io->use_scaling) {
|
||||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
int scaled_width = options->scaled_width;
|
||||||
|
int scaled_height = options->scaled_height;
|
||||||
|
if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
io->scaled_width = options->scaled_width;
|
io->scaled_width = scaled_width;
|
||||||
io->scaled_height = options->scaled_height;
|
io->scaled_height = scaled_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
io->bypass_filtering = options && options->bypass_filtering;
|
io->bypass_filtering = (options != NULL) && options->bypass_filtering;
|
||||||
|
|
||||||
// Fancy upsampler
|
// Fancy upsampler
|
||||||
#ifdef FANCY_UPSAMPLING
|
#ifdef FANCY_UPSAMPLING
|
||||||
@@ -833,4 +841,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -18,15 +18,18 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../utils/rescaler.h"
|
#include "../utils/rescaler_utils.h"
|
||||||
#include "./decode_vp8.h"
|
#include "./vp8_dec.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// WebPDecParams: Decoding output parameters. Transient internal object.
|
// WebPDecParams: Decoding output parameters. Transient internal object.
|
||||||
|
|
||||||
typedef struct WebPDecParams WebPDecParams;
|
typedef struct WebPDecParams WebPDecParams;
|
||||||
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
|
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
|
||||||
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
|
typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p,
|
||||||
|
int expected_num_out_lines);
|
||||||
|
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos,
|
||||||
|
int max_out_lines);
|
||||||
|
|
||||||
struct WebPDecParams {
|
struct WebPDecParams {
|
||||||
WebPDecBuffer* output; // output buffer.
|
WebPDecBuffer* output; // output buffer.
|
||||||
@@ -35,12 +38,12 @@ struct WebPDecParams {
|
|||||||
|
|
||||||
int last_y; // coordinate of the line that was last output
|
int last_y; // coordinate of the line that was last output
|
||||||
const WebPDecoderOptions* options; // if not NULL, use alt decoding features
|
const WebPDecoderOptions* options; // if not NULL, use alt decoding features
|
||||||
// rescalers
|
|
||||||
WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
|
WebPRescaler* scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers
|
||||||
void* memory; // overall scratch memory for the output work.
|
void* memory; // overall scratch memory for the output work.
|
||||||
|
|
||||||
OutputFunc emit; // output RGB or YUV samples
|
OutputFunc emit; // output RGB or YUV samples
|
||||||
OutputFunc emit_alpha; // output alpha channel
|
OutputAlphaFunc emit_alpha; // output alpha channel
|
||||||
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
|
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,13 +107,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
|||||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
|
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
|
||||||
|
|
||||||
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
|
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
|
||||||
// memory (still held by 'src').
|
// memory (still held by 'src'). No pixels are copied.
|
||||||
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||||
WebPDecBuffer* const dst);
|
WebPDecBuffer* const dst);
|
||||||
|
|
||||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||||
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
|
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
|
||||||
|
|
||||||
|
// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns
|
||||||
|
// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy.
|
||||||
|
VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src,
|
||||||
|
WebPDecBuffer* const dst);
|
||||||
|
|
||||||
|
// Returns true if decoding will be slow with the current configuration
|
||||||
|
// and bitstream features.
|
||||||
|
int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
|
||||||
|
const WebPBitstreamFeatures* const features);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
lib_LTLIBRARIES = libwebpdemux.la
|
lib_LTLIBRARIES = libwebpdemux.la
|
||||||
|
|
||||||
libwebpdemux_la_SOURCES =
|
libwebpdemux_la_SOURCES =
|
||||||
libwebpdemux_la_SOURCES += demux.c
|
libwebpdemux_la_SOURCES += anim_decode.c demux.c
|
||||||
|
|
||||||
libwebpdemuxinclude_HEADERS =
|
libwebpdemuxinclude_HEADERS =
|
||||||
libwebpdemuxinclude_HEADERS += ../webp/demux.h
|
libwebpdemuxinclude_HEADERS += ../webp/demux.h
|
||||||
@@ -9,6 +9,6 @@ libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
|
|||||||
libwebpdemuxinclude_HEADERS += ../webp/types.h
|
libwebpdemuxinclude_HEADERS += ../webp/types.h
|
||||||
|
|
||||||
libwebpdemux_la_LIBADD = ../libwebp.la
|
libwebpdemux_la_LIBADD = ../libwebp.la
|
||||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 1:1:0
|
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:2:0
|
||||||
libwebpdemuxincludedir = $(includedir)/webp
|
libwebpdemuxincludedir = $(includedir)/webp
|
||||||
pkgconfig_DATA = libwebpdemux.pc
|
pkgconfig_DATA = libwebpdemux.pc
|
||||||
|
|||||||
454
src/demux/anim_decode.c
Normal file
454
src/demux/anim_decode.c
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
// 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// AnimDecoder implementation.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "../webp/config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
#include "../webp/decode.h"
|
||||||
|
#include "../webp/demux.h"
|
||||||
|
|
||||||
|
#define NUM_CHANNELS 4
|
||||||
|
|
||||||
|
typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
|
||||||
|
static void BlendPixelRowNonPremult(uint32_t* const src,
|
||||||
|
const uint32_t* const dst, int num_pixels);
|
||||||
|
static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
|
||||||
|
int num_pixels);
|
||||||
|
|
||||||
|
struct WebPAnimDecoder {
|
||||||
|
WebPDemuxer* demux_; // Demuxer created from given WebP bitstream.
|
||||||
|
WebPDecoderConfig config_; // Decoder config.
|
||||||
|
// Note: we use a pointer to a function blending multiple pixels at a time to
|
||||||
|
// allow possible inlining of per-pixel blending function.
|
||||||
|
BlendRowFunc blend_func_; // Pointer to the chose blend row function.
|
||||||
|
WebPAnimInfo info_; // Global info about the animation.
|
||||||
|
uint8_t* curr_frame_; // Current canvas (not disposed).
|
||||||
|
uint8_t* prev_frame_disposed_; // Previous canvas (properly disposed).
|
||||||
|
int prev_frame_timestamp_; // Previous frame timestamp (milliseconds).
|
||||||
|
WebPIterator prev_iter_; // Iterator object for previous frame.
|
||||||
|
int prev_frame_was_keyframe_; // True if previous frame was a keyframe.
|
||||||
|
int next_frame_; // Index of the next frame to be decoded
|
||||||
|
// (starting from 1).
|
||||||
|
};
|
||||||
|
|
||||||
|
static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) {
|
||||||
|
dec_options->color_mode = MODE_RGBA;
|
||||||
|
dec_options->use_threads = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
|
||||||
|
int abi_version) {
|
||||||
|
if (dec_options == NULL ||
|
||||||
|
WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DefaultDecoderOptions(dec_options);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
|
||||||
|
WebPAnimDecoder* const dec) {
|
||||||
|
WEBP_CSP_MODE mode;
|
||||||
|
WebPDecoderConfig* config = &dec->config_;
|
||||||
|
assert(dec_options != NULL);
|
||||||
|
|
||||||
|
mode = dec_options->color_mode;
|
||||||
|
if (mode != MODE_RGBA && mode != MODE_BGRA &&
|
||||||
|
mode != MODE_rgbA && mode != MODE_bgrA) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
|
||||||
|
? &BlendPixelRowNonPremult
|
||||||
|
: &BlendPixelRowPremult;
|
||||||
|
WebPInitDecoderConfig(config);
|
||||||
|
config->output.colorspace = mode;
|
||||||
|
config->output.is_external_memory = 1;
|
||||||
|
config->options.use_threads = dec_options->use_threads;
|
||||||
|
// Note: config->output.u.RGBA is set at the time of decoding each frame.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPAnimDecoder* WebPAnimDecoderNewInternal(
|
||||||
|
const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options,
|
||||||
|
int abi_version) {
|
||||||
|
WebPAnimDecoderOptions options;
|
||||||
|
WebPAnimDecoder* dec = NULL;
|
||||||
|
if (webp_data == NULL ||
|
||||||
|
WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: calloc() so that the pointer members are initialized to NULL.
|
||||||
|
dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||||
|
if (dec == NULL) goto Error;
|
||||||
|
|
||||||
|
if (dec_options != NULL) {
|
||||||
|
options = *dec_options;
|
||||||
|
} else {
|
||||||
|
DefaultDecoderOptions(&options);
|
||||||
|
}
|
||||||
|
if (!ApplyDecoderOptions(&options, dec)) goto Error;
|
||||||
|
|
||||||
|
dec->demux_ = WebPDemux(webp_data);
|
||||||
|
if (dec->demux_ == NULL) goto Error;
|
||||||
|
|
||||||
|
dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH);
|
||||||
|
dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT);
|
||||||
|
dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT);
|
||||||
|
dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
|
||||||
|
dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
|
||||||
|
|
||||||
|
// Note: calloc() because we fill frame with zeroes as well.
|
||||||
|
dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
|
||||||
|
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||||
|
if (dec->curr_frame_ == NULL) goto Error;
|
||||||
|
dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
|
||||||
|
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
|
||||||
|
if (dec->prev_frame_disposed_ == NULL) goto Error;
|
||||||
|
|
||||||
|
WebPAnimDecoderReset(dec);
|
||||||
|
return dec;
|
||||||
|
|
||||||
|
Error:
|
||||||
|
WebPAnimDecoderDelete(dec);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) {
|
||||||
|
if (dec == NULL || info == NULL) return 0;
|
||||||
|
*info = dec->info_;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the frame covers the full canvas.
|
||||||
|
static int IsFullFrame(int width, int height, int canvas_width,
|
||||||
|
int canvas_height) {
|
||||||
|
return (width == canvas_width && height == canvas_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the canvas to transparent.
|
||||||
|
static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
|
||||||
|
uint32_t canvas_height) {
|
||||||
|
const uint64_t size =
|
||||||
|
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
|
||||||
|
if (size != (size_t)size) return 0;
|
||||||
|
memset(buf, 0, (size_t)size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear given frame rectangle to transparent.
|
||||||
|
static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
|
||||||
|
int y_offset, int width, int height) {
|
||||||
|
int j;
|
||||||
|
assert(width * NUM_CHANNELS <= buf_stride);
|
||||||
|
buf += y_offset * buf_stride + x_offset * NUM_CHANNELS;
|
||||||
|
for (j = 0; j < height; ++j) {
|
||||||
|
memset(buf, 0, width * NUM_CHANNELS);
|
||||||
|
buf += buf_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy width * height pixels from 'src' to 'dst'.
|
||||||
|
static int CopyCanvas(const uint8_t* src, uint8_t* dst,
|
||||||
|
uint32_t width, uint32_t height) {
|
||||||
|
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
|
||||||
|
if (size != (size_t)size) return 0;
|
||||||
|
assert(src != NULL && dst != NULL);
|
||||||
|
memcpy(dst, src, (size_t)size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the current frame is a key-frame.
|
||||||
|
static int IsKeyFrame(const WebPIterator* const curr,
|
||||||
|
const WebPIterator* const prev,
|
||||||
|
int prev_frame_was_key_frame,
|
||||||
|
int canvas_width, int canvas_height) {
|
||||||
|
if (curr->frame_num == 1) {
|
||||||
|
return 1;
|
||||||
|
} else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) &&
|
||||||
|
IsFullFrame(curr->width, curr->height,
|
||||||
|
canvas_width, canvas_height)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
|
||||||
|
(IsFullFrame(prev->width, prev->height, canvas_width,
|
||||||
|
canvas_height) ||
|
||||||
|
prev_frame_was_key_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Blend a single channel of 'src' over 'dst', given their alpha channel values.
|
||||||
|
// 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha.
|
||||||
|
static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
|
||||||
|
uint32_t dst, uint8_t dst_a,
|
||||||
|
uint32_t scale, int shift) {
|
||||||
|
const uint8_t src_channel = (src >> shift) & 0xff;
|
||||||
|
const uint8_t dst_channel = (dst >> shift) & 0xff;
|
||||||
|
const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
|
||||||
|
assert(blend_unscaled < (1ULL << 32) / scale);
|
||||||
|
return (blend_unscaled * scale) >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
|
||||||
|
static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
|
||||||
|
const uint8_t src_a = (src >> 24) & 0xff;
|
||||||
|
|
||||||
|
if (src_a == 0) {
|
||||||
|
return dst;
|
||||||
|
} else {
|
||||||
|
const uint8_t dst_a = (dst >> 24) & 0xff;
|
||||||
|
// This is the approximate integer arithmetic for the actual formula:
|
||||||
|
// dst_factor_a = (dst_a * (255 - src_a)) / 255.
|
||||||
|
const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
|
||||||
|
const uint8_t blend_a = src_a + dst_factor_a;
|
||||||
|
const uint32_t scale = (1UL << 24) / blend_a;
|
||||||
|
|
||||||
|
const uint8_t blend_r =
|
||||||
|
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0);
|
||||||
|
const uint8_t blend_g =
|
||||||
|
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8);
|
||||||
|
const uint8_t blend_b =
|
||||||
|
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16);
|
||||||
|
assert(src_a + dst_factor_a < 256);
|
||||||
|
|
||||||
|
return (blend_r << 0) |
|
||||||
|
(blend_g << 8) |
|
||||||
|
(blend_b << 16) |
|
||||||
|
((uint32_t)blend_a << 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend 'num_pixels' in 'src' over 'dst' assuming they are NOT pre-multiplied
|
||||||
|
// by alpha.
|
||||||
|
static void BlendPixelRowNonPremult(uint32_t* const src,
|
||||||
|
const uint32_t* const dst, int num_pixels) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_pixels; ++i) {
|
||||||
|
const uint8_t src_alpha = (src[i] >> 24) & 0xff;
|
||||||
|
if (src_alpha != 0xff) {
|
||||||
|
src[i] = BlendPixelNonPremult(src[i], dst[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individually multiply each channel in 'pix' by 'scale'.
|
||||||
|
static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
|
||||||
|
uint32_t mask = 0x00FF00FF;
|
||||||
|
uint32_t rb = ((pix & mask) * scale) >> 8;
|
||||||
|
uint32_t ag = ((pix >> 8) & mask) * scale;
|
||||||
|
return (rb & mask) | (ag & ~mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
|
||||||
|
static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
|
||||||
|
const uint8_t src_a = (src >> 24) & 0xff;
|
||||||
|
return src + ChannelwiseMultiply(dst, 256 - src_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend 'num_pixels' in 'src' over 'dst' assuming they are pre-multiplied by
|
||||||
|
// alpha.
|
||||||
|
static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
|
||||||
|
int num_pixels) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_pixels; ++i) {
|
||||||
|
const uint8_t src_alpha = (src[i] >> 24) & 0xff;
|
||||||
|
if (src_alpha != 0xff) {
|
||||||
|
src[i] = BlendPixelPremult(src[i], dst[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns two ranges (<left, width> pairs) at row 'canvas_y', that belong to
|
||||||
|
// 'src' but not 'dst'. A point range is empty if the corresponding width is 0.
|
||||||
|
static void FindBlendRangeAtRow(const WebPIterator* const src,
|
||||||
|
const WebPIterator* const dst, int canvas_y,
|
||||||
|
int* const left1, int* const width1,
|
||||||
|
int* const left2, int* const width2) {
|
||||||
|
const int src_max_x = src->x_offset + src->width;
|
||||||
|
const int dst_max_x = dst->x_offset + dst->width;
|
||||||
|
const int dst_max_y = dst->y_offset + dst->height;
|
||||||
|
assert(canvas_y >= src->y_offset && canvas_y < (src->y_offset + src->height));
|
||||||
|
*left1 = -1;
|
||||||
|
*width1 = 0;
|
||||||
|
*left2 = -1;
|
||||||
|
*width2 = 0;
|
||||||
|
|
||||||
|
if (canvas_y < dst->y_offset || canvas_y >= dst_max_y ||
|
||||||
|
src->x_offset >= dst_max_x || src_max_x <= dst->x_offset) {
|
||||||
|
*left1 = src->x_offset;
|
||||||
|
*width1 = src->width;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src->x_offset < dst->x_offset) {
|
||||||
|
*left1 = src->x_offset;
|
||||||
|
*width1 = dst->x_offset - src->x_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_max_x > dst_max_x) {
|
||||||
|
*left2 = dst_max_x;
|
||||||
|
*width2 = src_max_x - dst_max_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
|
||||||
|
uint8_t** buf_ptr, int* timestamp_ptr) {
|
||||||
|
WebPIterator iter;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
int is_key_frame;
|
||||||
|
int timestamp;
|
||||||
|
BlendRowFunc blend_row;
|
||||||
|
|
||||||
|
if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0;
|
||||||
|
if (!WebPAnimDecoderHasMoreFrames(dec)) return 0;
|
||||||
|
|
||||||
|
width = dec->info_.canvas_width;
|
||||||
|
height = dec->info_.canvas_height;
|
||||||
|
blend_row = dec->blend_func_;
|
||||||
|
|
||||||
|
// Get compressed frame.
|
||||||
|
if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
timestamp = dec->prev_frame_timestamp_ + iter.duration;
|
||||||
|
|
||||||
|
// Initialize.
|
||||||
|
is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
|
||||||
|
dec->prev_frame_was_keyframe_, width, height);
|
||||||
|
if (is_key_frame) {
|
||||||
|
if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
|
||||||
|
width, height)) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode.
|
||||||
|
{
|
||||||
|
const uint8_t* in = iter.fragment.bytes;
|
||||||
|
const size_t in_size = iter.fragment.size;
|
||||||
|
const size_t out_offset =
|
||||||
|
(iter.y_offset * width + iter.x_offset) * NUM_CHANNELS;
|
||||||
|
WebPDecoderConfig* const config = &dec->config_;
|
||||||
|
WebPRGBABuffer* const buf = &config->output.u.RGBA;
|
||||||
|
buf->stride = NUM_CHANNELS * width;
|
||||||
|
buf->size = buf->stride * iter.height;
|
||||||
|
buf->rgba = dec->curr_frame_ + out_offset;
|
||||||
|
|
||||||
|
if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// During the decoding of current frame, we may have set some pixels to be
|
||||||
|
// transparent (i.e. alpha < 255). However, the value of each of these
|
||||||
|
// pixels should have been determined by blending it against the value of
|
||||||
|
// that pixel in the previous frame if blending method of is WEBP_MUX_BLEND.
|
||||||
|
if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND &&
|
||||||
|
!is_key_frame) {
|
||||||
|
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) {
|
||||||
|
int y;
|
||||||
|
// Blend transparent pixels with pixels in previous canvas.
|
||||||
|
for (y = 0; y < iter.height; ++y) {
|
||||||
|
const size_t offset =
|
||||||
|
(iter.y_offset + y) * width + iter.x_offset;
|
||||||
|
blend_row((uint32_t*)dec->curr_frame_ + offset,
|
||||||
|
(uint32_t*)dec->prev_frame_disposed_ + offset, iter.width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int y;
|
||||||
|
assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
|
||||||
|
// We need to blend a transparent pixel with its value just after
|
||||||
|
// initialization. That is, blend it with:
|
||||||
|
// * Fully transparent pixel if it belongs to prevRect <-- No-op.
|
||||||
|
// * The pixel in the previous canvas otherwise <-- Need alpha-blending.
|
||||||
|
for (y = 0; y < iter.height; ++y) {
|
||||||
|
const int canvas_y = iter.y_offset + y;
|
||||||
|
int left1, width1, left2, width2;
|
||||||
|
FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1,
|
||||||
|
&left2, &width2);
|
||||||
|
if (width1 > 0) {
|
||||||
|
const size_t offset1 = canvas_y * width + left1;
|
||||||
|
blend_row((uint32_t*)dec->curr_frame_ + offset1,
|
||||||
|
(uint32_t*)dec->prev_frame_disposed_ + offset1, width1);
|
||||||
|
}
|
||||||
|
if (width2 > 0) {
|
||||||
|
const size_t offset2 = canvas_y * width + left2;
|
||||||
|
blend_row((uint32_t*)dec->curr_frame_ + offset2,
|
||||||
|
(uint32_t*)dec->prev_frame_disposed_ + offset2, width2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update info of the previous frame and dispose it for the next iteration.
|
||||||
|
dec->prev_frame_timestamp_ = timestamp;
|
||||||
|
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||||
|
dec->prev_iter_ = iter;
|
||||||
|
dec->prev_frame_was_keyframe_ = is_key_frame;
|
||||||
|
CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
|
||||||
|
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||||
|
ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
|
||||||
|
dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
|
||||||
|
dec->prev_iter_.width, dec->prev_iter_.height);
|
||||||
|
}
|
||||||
|
++dec->next_frame_;
|
||||||
|
|
||||||
|
// All OK, fill in the values.
|
||||||
|
*buf_ptr = dec->curr_frame_;
|
||||||
|
*timestamp_ptr = timestamp;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
Error:
|
||||||
|
WebPDemuxReleaseIterator(&iter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
|
||||||
|
if (dec == NULL) return 0;
|
||||||
|
return (dec->next_frame_ <= (int)dec->info_.frame_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
|
||||||
|
if (dec != NULL) {
|
||||||
|
dec->prev_frame_timestamp_ = 0;
|
||||||
|
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||||
|
memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
|
||||||
|
dec->prev_frame_was_keyframe_ = 0;
|
||||||
|
dec->next_frame_ = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
|
||||||
|
if (dec == NULL) return NULL;
|
||||||
|
return dec->demux_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
|
||||||
|
if (dec != NULL) {
|
||||||
|
WebPDemuxReleaseIterator(&dec->prev_iter_);
|
||||||
|
WebPDemuxDelete(dec->demux_);
|
||||||
|
WebPSafeFree(dec->curr_frame_);
|
||||||
|
WebPSafeFree(dec->prev_frame_disposed_);
|
||||||
|
WebPSafeFree(dec);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
#include "../webp/format_constants.h"
|
#include "../webp/format_constants.h"
|
||||||
|
|
||||||
#define DMUX_MAJ_VERSION 0
|
#define DMUX_MAJ_VERSION 0
|
||||||
#define DMUX_MIN_VERSION 2
|
#define DMUX_MIN_VERSION 3
|
||||||
#define DMUX_REV_VERSION 1
|
#define DMUX_REV_VERSION 2
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t start_; // start location of the data
|
size_t start_; // start location of the data
|
||||||
@@ -47,8 +47,7 @@ typedef struct Frame {
|
|||||||
int duration_;
|
int duration_;
|
||||||
WebPMuxAnimDispose dispose_method_;
|
WebPMuxAnimDispose dispose_method_;
|
||||||
WebPMuxAnimBlend blend_method_;
|
WebPMuxAnimBlend blend_method_;
|
||||||
int is_fragment_; // this is a frame fragment (and not a full frame).
|
int frame_num_;
|
||||||
int frame_num_; // the referent frame number for use in assembling fragments.
|
|
||||||
int complete_; // img_components_ contains a full image.
|
int complete_; // img_components_ contains a full image.
|
||||||
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
|
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
|
||||||
struct Frame* next_;
|
struct Frame* next_;
|
||||||
@@ -193,6 +192,19 @@ static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetFrameInfo(size_t start_offset, size_t size,
|
||||||
|
int frame_num, int complete,
|
||||||
|
const WebPBitstreamFeatures* const features,
|
||||||
|
Frame* const frame) {
|
||||||
|
frame->img_components_[0].offset_ = start_offset;
|
||||||
|
frame->img_components_[0].size_ = size;
|
||||||
|
frame->width_ = features->width;
|
||||||
|
frame->height_ = features->height;
|
||||||
|
frame->has_alpha_ |= features->has_alpha;
|
||||||
|
frame->frame_num_ = frame_num;
|
||||||
|
frame->complete_ = complete;
|
||||||
|
}
|
||||||
|
|
||||||
// Store image bearing chunks to 'frame'.
|
// Store image bearing chunks to 'frame'.
|
||||||
static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||||
MemBuffer* const mem, Frame* const frame) {
|
MemBuffer* const mem, Frame* const frame) {
|
||||||
@@ -248,13 +260,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
|||||||
return PARSE_ERROR;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
++image_chunks;
|
++image_chunks;
|
||||||
frame->img_components_[0].offset_ = chunk_start_offset;
|
SetFrameInfo(chunk_start_offset, chunk_size, frame_num,
|
||||||
frame->img_components_[0].size_ = chunk_size;
|
status == PARSE_OK, &features, frame);
|
||||||
frame->width_ = features.width;
|
|
||||||
frame->height_ = features.height;
|
|
||||||
frame->has_alpha_ |= features.has_alpha;
|
|
||||||
frame->frame_num_ = frame_num;
|
|
||||||
frame->complete_ = (status == PARSE_OK);
|
|
||||||
Skip(mem, payload_available);
|
Skip(mem, payload_available);
|
||||||
} else {
|
} else {
|
||||||
goto Done;
|
goto Done;
|
||||||
@@ -337,42 +344,6 @@ static ParseStatus ParseAnimationFrame(
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
// Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
|
|
||||||
// 'fragment_chunk_size' is the previously validated, padded chunk size.
|
|
||||||
static ParseStatus ParseFragment(WebPDemuxer* const dmux,
|
|
||||||
uint32_t fragment_chunk_size) {
|
|
||||||
const int frame_num = 1; // All fragments belong to the 1st (and only) frame.
|
|
||||||
const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
|
|
||||||
const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
|
|
||||||
int added_fragment = 0;
|
|
||||||
MemBuffer* const mem = &dmux->mem_;
|
|
||||||
Frame* frame;
|
|
||||||
ParseStatus status =
|
|
||||||
NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
|
|
||||||
if (status != PARSE_OK) return status;
|
|
||||||
|
|
||||||
frame->is_fragment_ = 1;
|
|
||||||
frame->x_offset_ = 2 * ReadLE24s(mem);
|
|
||||||
frame->y_offset_ = 2 * ReadLE24s(mem);
|
|
||||||
|
|
||||||
// Store a fragment only if the 'fragments' flag is set and there is some
|
|
||||||
// data available.
|
|
||||||
status = StoreFrame(frame_num, frgm_payload_size, mem, frame);
|
|
||||||
if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) {
|
|
||||||
added_fragment = AddFrame(dmux, frame);
|
|
||||||
if (!added_fragment) {
|
|
||||||
status = PARSE_ERROR;
|
|
||||||
} else {
|
|
||||||
dmux->num_frames_ = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!added_fragment) WebPSafeFree(frame);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
|
|
||||||
// General chunk storage, starting with the header at 'start_offset', allowing
|
// General chunk storage, starting with the header at 'start_offset', allowing
|
||||||
// the user to request the payload via a fourcc string. 'size' includes the
|
// the user to request the payload via a fourcc string. 'size' includes the
|
||||||
// header and the unpadded payload size.
|
// header and the unpadded payload size.
|
||||||
@@ -513,12 +484,6 @@ static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
|||||||
status = ParseAnimationFrame(dmux, chunk_size_padded);
|
status = ParseAnimationFrame(dmux, chunk_size_padded);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
||||||
case MKFOURCC('F', 'R', 'G', 'M'): {
|
|
||||||
status = ParseFragment(dmux, chunk_size_padded);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
case MKFOURCC('I', 'C', 'C', 'P'): {
|
case MKFOURCC('I', 'C', 'C', 'P'): {
|
||||||
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
|
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
|
||||||
goto Skip;
|
goto Skip;
|
||||||
@@ -606,8 +571,6 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
|
|||||||
|
|
||||||
// If 'exact' is true, check that the image resolution matches the canvas.
|
// If 'exact' is true, check that the image resolution matches the canvas.
|
||||||
// If 'exact' is false, check that the x/y offsets do not exceed the canvas.
|
// If 'exact' is false, check that the x/y offsets do not exceed the canvas.
|
||||||
// TODO(jzern): this is insufficient in the fragmented image case if the
|
|
||||||
// expectation is that the fragments completely cover the canvas.
|
|
||||||
static int CheckFrameBounds(const Frame* const frame, int exact,
|
static int CheckFrameBounds(const Frame* const frame, int exact,
|
||||||
int canvas_width, int canvas_height) {
|
int canvas_width, int canvas_height) {
|
||||||
if (exact) {
|
if (exact) {
|
||||||
@@ -627,7 +590,6 @@ static int CheckFrameBounds(const Frame* const frame, int exact,
|
|||||||
|
|
||||||
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
||||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||||
const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
|
|
||||||
const Frame* f = dmux->frames_;
|
const Frame* f = dmux->frames_;
|
||||||
|
|
||||||
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
|
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||||
@@ -635,22 +597,17 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
|||||||
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
|
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
|
||||||
if (dmux->loop_count_ < 0) return 0;
|
if (dmux->loop_count_ < 0) return 0;
|
||||||
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
|
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
|
||||||
#ifndef WEBP_EXPERIMENTAL_FEATURES
|
if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
|
||||||
if (is_fragmented) return 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (f != NULL) {
|
while (f != NULL) {
|
||||||
const int cur_frame_set = f->frame_num_;
|
const int cur_frame_set = f->frame_num_;
|
||||||
int frame_count = 0, fragment_count = 0;
|
int frame_count = 0;
|
||||||
|
|
||||||
// Check frame properties and if the image is composed of fragments that
|
// Check frame properties.
|
||||||
// each fragment came from a fragment.
|
|
||||||
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
|
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
|
||||||
const ChunkData* const image = f->img_components_;
|
const ChunkData* const image = f->img_components_;
|
||||||
const ChunkData* const alpha = f->img_components_ + 1;
|
const ChunkData* const alpha = f->img_components_ + 1;
|
||||||
|
|
||||||
if (is_fragmented && !f->is_fragment_) return 0;
|
|
||||||
if (!is_fragmented && f->is_fragment_) return 0;
|
|
||||||
if (!is_animation && f->frame_num_ > 1) return 0;
|
if (!is_animation && f->frame_num_ > 1) return 0;
|
||||||
|
|
||||||
if (f->complete_) {
|
if (f->complete_) {
|
||||||
@@ -675,16 +632,13 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (f->width_ > 0 && f->height_ > 0 &&
|
if (f->width_ > 0 && f->height_ > 0 &&
|
||||||
!CheckFrameBounds(f, !(is_animation || is_fragmented),
|
!CheckFrameBounds(f, !is_animation,
|
||||||
dmux->canvas_width_, dmux->canvas_height_)) {
|
dmux->canvas_width_, dmux->canvas_height_)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment_count += f->is_fragment_;
|
|
||||||
++frame_count;
|
++frame_count;
|
||||||
}
|
}
|
||||||
if (!is_fragmented && frame_count > 1) return 0;
|
|
||||||
if (fragment_count > 0 && frame_count != fragment_count) return 0;
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -703,6 +657,41 @@ static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
|
|||||||
dmux->mem_ = *mem;
|
dmux->mem_ = *mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
|
||||||
|
WebPDemuxer** demuxer) {
|
||||||
|
WebPBitstreamFeatures features;
|
||||||
|
const VP8StatusCode status =
|
||||||
|
WebPGetFeatures(mem->buf_, mem->buf_size_, &features);
|
||||||
|
*demuxer = NULL;
|
||||||
|
if (status != VP8_STATUS_OK) {
|
||||||
|
return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA
|
||||||
|
: PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
|
||||||
|
Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
|
||||||
|
if (dmux == NULL || frame == NULL) goto Error;
|
||||||
|
InitDemux(dmux, mem);
|
||||||
|
SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features,
|
||||||
|
frame);
|
||||||
|
if (!AddFrame(dmux, frame)) goto Error;
|
||||||
|
dmux->state_ = WEBP_DEMUX_DONE;
|
||||||
|
dmux->canvas_width_ = frame->width_;
|
||||||
|
dmux->canvas_height_ = frame->height_;
|
||||||
|
dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
|
||||||
|
dmux->num_frames_ = 1;
|
||||||
|
assert(IsValidSimpleFormat(dmux));
|
||||||
|
*demuxer = dmux;
|
||||||
|
return PARSE_OK;
|
||||||
|
|
||||||
|
Error:
|
||||||
|
WebPSafeFree(dmux);
|
||||||
|
WebPSafeFree(frame);
|
||||||
|
return PARSE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
||||||
WebPDemuxState* state, int version) {
|
WebPDemuxState* state, int version) {
|
||||||
const ChunkParser* parser;
|
const ChunkParser* parser;
|
||||||
@@ -719,6 +708,15 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
|||||||
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
|
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
|
||||||
status = ReadHeader(&mem);
|
status = ReadHeader(&mem);
|
||||||
if (status != PARSE_OK) {
|
if (status != PARSE_OK) {
|
||||||
|
// If parsing of the webp file header fails attempt to handle a raw
|
||||||
|
// VP8/VP8L frame. Note 'allow_partial' is ignored in this case.
|
||||||
|
if (status == PARSE_ERROR) {
|
||||||
|
status = CreateRawImageDemuxer(&mem, &dmux);
|
||||||
|
if (status == PARSE_OK) {
|
||||||
|
if (state != NULL) *state = WEBP_DEMUX_DONE;
|
||||||
|
return dmux;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (state != NULL) {
|
if (state != NULL) {
|
||||||
*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
|
*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
|
||||||
: WEBP_DEMUX_PARSE_ERROR;
|
: WEBP_DEMUX_PARSE_ERROR;
|
||||||
@@ -790,8 +788,6 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Frame iteration
|
// Frame iteration
|
||||||
|
|
||||||
// Find the first 'frame_num' frame. There may be multiple such frames in a
|
|
||||||
// fragmented frame.
|
|
||||||
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
|
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
|
||||||
const Frame* f;
|
const Frame* f;
|
||||||
for (f = dmux->frames_; f != NULL; f = f->next_) {
|
for (f = dmux->frames_; f != NULL; f = f->next_) {
|
||||||
@@ -800,21 +796,6 @@ static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns fragment 'fragment_num' and the total count.
|
|
||||||
static const Frame* GetFragment(
|
|
||||||
const Frame* const frame_set, int fragment_num, int* const count) {
|
|
||||||
const int this_frame = frame_set->frame_num_;
|
|
||||||
const Frame* f = frame_set;
|
|
||||||
const Frame* fragment = NULL;
|
|
||||||
int total;
|
|
||||||
|
|
||||||
for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
|
|
||||||
if (++total == fragment_num) fragment = f;
|
|
||||||
}
|
|
||||||
*count = total;
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
||||||
const Frame* const frame,
|
const Frame* const frame,
|
||||||
size_t* const data_size) {
|
size_t* const data_size) {
|
||||||
@@ -841,34 +822,27 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
|||||||
|
|
||||||
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
|
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
|
||||||
static int SynthesizeFrame(const WebPDemuxer* const dmux,
|
static int SynthesizeFrame(const WebPDemuxer* const dmux,
|
||||||
const Frame* const first_frame,
|
const Frame* const frame,
|
||||||
int fragment_num, WebPIterator* const iter) {
|
WebPIterator* const iter) {
|
||||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||||
int num_fragments;
|
|
||||||
size_t payload_size = 0;
|
size_t payload_size = 0;
|
||||||
const Frame* const fragment =
|
const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
|
||||||
GetFragment(first_frame, fragment_num, &num_fragments);
|
|
||||||
const uint8_t* const payload =
|
|
||||||
GetFramePayload(mem_buf, fragment, &payload_size);
|
|
||||||
if (payload == NULL) return 0;
|
if (payload == NULL) return 0;
|
||||||
assert(first_frame != NULL);
|
assert(frame != NULL);
|
||||||
|
|
||||||
iter->frame_num = first_frame->frame_num_;
|
iter->frame_num = frame->frame_num_;
|
||||||
iter->num_frames = dmux->num_frames_;
|
iter->num_frames = dmux->num_frames_;
|
||||||
iter->fragment_num = fragment_num;
|
iter->x_offset = frame->x_offset_;
|
||||||
iter->num_fragments = num_fragments;
|
iter->y_offset = frame->y_offset_;
|
||||||
iter->x_offset = fragment->x_offset_;
|
iter->width = frame->width_;
|
||||||
iter->y_offset = fragment->y_offset_;
|
iter->height = frame->height_;
|
||||||
iter->width = fragment->width_;
|
iter->has_alpha = frame->has_alpha_;
|
||||||
iter->height = fragment->height_;
|
iter->duration = frame->duration_;
|
||||||
iter->has_alpha = fragment->has_alpha_;
|
iter->dispose_method = frame->dispose_method_;
|
||||||
iter->duration = fragment->duration_;
|
iter->blend_method = frame->blend_method_;
|
||||||
iter->dispose_method = fragment->dispose_method_;
|
iter->complete = frame->complete_;
|
||||||
iter->blend_method = fragment->blend_method_;
|
|
||||||
iter->complete = fragment->complete_;
|
|
||||||
iter->fragment.bytes = payload;
|
iter->fragment.bytes = payload;
|
||||||
iter->fragment.size = payload_size;
|
iter->fragment.size = payload_size;
|
||||||
// TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,7 +856,7 @@ static int SetFrame(int frame_num, WebPIterator* const iter) {
|
|||||||
frame = GetFrame(dmux, frame_num);
|
frame = GetFrame(dmux, frame_num);
|
||||||
if (frame == NULL) return 0;
|
if (frame == NULL) return 0;
|
||||||
|
|
||||||
return SynthesizeFrame(dmux, frame, 1, iter);
|
return SynthesizeFrame(dmux, frame, iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
|
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
|
||||||
@@ -904,17 +878,6 @@ int WebPDemuxPrevFrame(WebPIterator* iter) {
|
|||||||
return SetFrame(iter->frame_num - 1, iter);
|
return SetFrame(iter->frame_num - 1, iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
|
|
||||||
if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
|
|
||||||
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
|
|
||||||
const Frame* const frame = GetFrame(dmux, iter->frame_num);
|
|
||||||
if (frame == NULL) return 0;
|
|
||||||
|
|
||||||
return SynthesizeFrame(dmux, frame, fragment_num, iter);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebPDemuxReleaseIterator(WebPIterator* iter) {
|
void WebPDemuxReleaseIterator(WebPIterator* iter) {
|
||||||
(void)iter;
|
(void)iter;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user