mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 21:33:04 +04:00
Compare commits
995 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9365817bfd | |||
| d656d39ba2 | |||
| 02db592014 | |||
| 3203635765 | |||
| cddf868572 | |||
| da8dbf6cf5 | |||
| 34046ec682 | |||
| daee982106 | |||
| f670a99270 | |||
| eed5cfba7d | |||
| 7b8e6307f8 | |||
| 823353d3ac | |||
| 639e7367a7 | |||
| 2863c9fe19 | |||
| 37c8f217a7 | |||
| c8783f3e23 | |||
| 9aaf6a66a3 | |||
| ced9d2357c | |||
| 9611aa81b2 | |||
| b10bd4b65d | |||
| 8aa3011f2d | |||
| 7bb9237d99 | |||
| 11a726ceb3 | |||
| c3586b8534 | |||
| 7621cc19dc | |||
| 4f01ad8517 | |||
| 5590aea411 | |||
| 8b7cb8a3f1 | |||
| 1b8d363231 | |||
| 3f02f569af | |||
| 108188e42f | |||
| 2a33f452b0 | |||
| f42b7d03b4 | |||
| 267fdc4c91 | |||
| 32d1eb36df | |||
| d5323ce848 | |||
| 34c0ab5878 | |||
| 8f7181429f | |||
| 921f06eb6f | |||
| d06619ab15 | |||
| 425e92e5ca | |||
| 5ca7dcc6e7 | |||
| 1f23202ad8 | |||
| 40e1f2fc03 | |||
| e5aa213554 | |||
| cdf2a59afa | |||
| 6320ede98e | |||
| c235bbfcb4 | |||
| c5a6846866 | |||
| 36b00e69ab | |||
| 3ce1bca670 | |||
| 444593014f | |||
| 6ea6e4bceb | |||
| 2db45c8a85 | |||
| a84a5e8f1a | |||
| 8bd2e9816a | |||
| 638a01a014 | |||
| 90d2f8b5fe | |||
| a6d88b4d1f | |||
| e0393f8557 | |||
| 3f72ff215e | |||
| 64b483c65b | |||
| 0c08094536 | |||
| fab4f4b9d5 | |||
| 72a765d766 | |||
| 2ac819018d | |||
| 5f1b6ee889 | |||
| 27c7ef28a7 | |||
| ac49a17a82 | |||
| fb1dcdd17d | |||
| b84cbeec8c | |||
| f6a33c4826 | |||
| f1a56cb4b7 | |||
| 9087778b06 | |||
| ace0701a46 | |||
| ca9622145e | |||
| 9d4cd352af | |||
| aa75b6067c | |||
| 511e50c19c | |||
| 324851882a | |||
| da0960321b | |||
| b9a2d7b600 | |||
| a769d69a9d | |||
| b46f5b1b38 | |||
| 08db55fb62 | |||
| 3c748ccf10 | |||
| 20f603a217 | |||
| 8088d6785a | |||
| b8b8047448 | |||
| 35a1ecef2a | |||
| ed10383359 | |||
| dcf3d988d5 | |||
| 7567eb8433 | |||
| c5faa9aefa | |||
| 98849151a2 | |||
| bbb14d3746 | |||
| ec321e651f | |||
| 2ae849091c | |||
| 8b3d6603d5 | |||
| 00dd433368 | |||
| 82ec76c123 | |||
| 1c4fb418cf | |||
| 58c5be0002 | |||
| 9e9f57c139 | |||
| f8a75c4361 | |||
| a87d6b098f | |||
| 22d2207d41 | |||
| 9ee60dbff2 | |||
| 121789f78e | |||
| 32d4af36e2 | |||
| b991665b5d | |||
| eff47b1260 | |||
| 04f8194327 | |||
| 16d1bbf2ea | |||
| 650830b9d6 | |||
| 45cbc512bf | |||
| fd91072990 | |||
| 3611396c0b | |||
| ef2e5a9f82 | |||
| 48924cd874 | |||
| 986d27e49c | |||
| b4f1102455 | |||
| e63feba8e2 | |||
| 93091ba203 | |||
| 623de337e8 | |||
| b500ae2d21 | |||
| 006966e629 | |||
| 07aff8e85f | |||
| e49c429162 | |||
| 3d7fd4132b | |||
| ee54bafe6b | |||
| e03569f3a8 | |||
| 382e38941c | |||
| 14de8ac951 | |||
| 6031c79197 | |||
| 7f12836df9 | |||
| 4f9871817a | |||
| 93729784bb | |||
| ef692350e0 | |||
| 09db01af5b | |||
| 8c27ceb9de | |||
| b3cd2448cd | |||
| c60b7d76c5 | |||
| 6b7a1d4dde | |||
| d7597a8610 | |||
| 460e9bf0af | |||
| 23f4bff742 | |||
| cc021e555b | |||
| a084501ecf | |||
| 35a2f2fef9 | |||
| 51e16bbe81 | |||
| 25020f2672 | |||
| fa7e7e0ff9 | |||
| 259147e32c | |||
| d649d1388e | |||
| 1c4c58d1b5 | |||
| 63b30554ad | |||
| d27009c775 | |||
| 4eb5242e16 | |||
| 5770b961a1 | |||
| 0303fa0a19 | |||
| 74127d44d0 | |||
| 92c0709c49 | |||
| e74fed5c84 | |||
| d29c299cdb | |||
| f0e3431bc7 | |||
| 16368a275b | |||
| 5f20e802d2 | |||
| 437ca0b62a | |||
| 05acf40aff | |||
| 3e3e2dd512 | |||
| 2c30f358dc | |||
| 6f3d881d74 | |||
| d3ebe665e0 | |||
| 9067310166 | |||
| 9496fe40ec | |||
| 87b569d812 | |||
| 8b664d6122 | |||
| 43b03b6517 | |||
| a9cc87636f | |||
| db35e13b46 | |||
| 594652cac1 | |||
| c06f3d63e8 | |||
| 68d01972fe | |||
| 8bf3f6bfcb | |||
| 1f4067112f | |||
| f1c16f42b4 | |||
| 037d8fbdcd | |||
| 3ea02e4fcc | |||
| b21b694444 | |||
| 7580db09ad | |||
| 939c8e8a99 | |||
| 5ad3ddc1b6 | |||
| bd215fbf33 | |||
| 26e9b42a44 | |||
| 4f35a592a1 | |||
| 9b902adea6 | |||
| 29593635ed | |||
| 2ad612f475 | |||
| cf86f88c71 | |||
| 234ad8fbee | |||
| e23b59da5c | |||
| 12a1c127a9 | |||
| 078b4cc971 | |||
| 9fa90c8851 | |||
| cc52523375 | |||
| f49f056d29 | |||
| bd1334a83f | |||
| e72e34ec36 | |||
| c0e456dee2 | |||
| d121d1c528 | |||
| 3a5e036feb | |||
| 2de1aac665 | |||
| fbafc700ea | |||
| 2ddbcc3c4f | |||
| 94848a3e1f | |||
| a3189e36c0 | |||
| f0c2da32f2 | |||
| 3dee87b697 | |||
| c3bea59a76 | |||
| 60f3ca458a | |||
| 772a8188c0 | |||
| 47c9bb7ba8 | |||
| 5b63399197 | |||
| daf3fabe0d | |||
| e665be1d70 | |||
| 0213b508dc | |||
| ea93bcc347 | |||
| e3c0d11b55 | |||
| f71ea4dfe9 | |||
| 5c0a287ce8 | |||
| 781515c514 | |||
| 515e01e649 | |||
| 1887dcb397 | |||
| fc84c48bd7 | |||
| fd7e516b65 | |||
| 71517a910a | |||
| 1961bb1857 | |||
| 31c79668c8 | |||
| cc47ee3b43 | |||
| 7b8d107384 | |||
| 59798b3f7c | |||
| 0e1d65d3b1 | |||
| 125abe2fe4 | |||
| e269ef96cb | |||
| a426a65b41 | |||
| a113e8f088 | |||
| ebd98eaf3a | |||
| 3933958b34 | |||
| 9bb17caa24 | |||
| bad149d77d | |||
| 59373a1ae1 | |||
| 79bb920dee | |||
| f935a16ef2 | |||
| f743603b0a | |||
| d22fb5f949 | |||
| 0d86681b88 | |||
| 0f42031b10 | |||
| c4a3278bab | |||
| fdba548858 | |||
| 89af053aed | |||
| c397361aa6 | |||
| c6a3a18894 | |||
| d54b1ad88a | |||
| 36918b3bb8 | |||
| 35de1c9fdf | |||
| 542cdb2c39 | |||
| 0f51155e79 | |||
| f4e18125b4 | |||
| 6e00c7651b | |||
| 89fcd6d8af | |||
| c26fd68236 | |||
| a0aee9d765 | |||
| f241a371b3 | |||
| ee257ffe9e | |||
| d962b3f499 | |||
| 6c164d86b9 | |||
| cc3fdd005e | |||
| 1d62a025b3 | |||
| 11af7b7e55 | |||
| ec49eb813c | |||
| f6b6fbf136 | |||
| 2a5e12ccb5 | |||
| ea8be57871 | |||
| 6473018d69 | |||
| affb60093d | |||
| 4a4d94f266 | |||
| bf8415283a | |||
| 2e056fbe8a | |||
| 9734ee13e5 | |||
| 057c10baac | |||
| 7bd3ccd929 | |||
| 37b1bc9d58 | |||
| 27649de50b | |||
| cf4e9e5ce2 | |||
| d72ddc8255 | |||
| 9ea2f5211e | |||
| 9f0bbdada2 | |||
| 4ac5f37d49 | |||
| d9ffc4c83e | |||
| 2ddaaf69fa | |||
| 19464a3ed8 | |||
| 246f47fe96 | |||
| 7c3577f7ae | |||
| 883d925f59 | |||
| 15a2c7724d | |||
| 1ce61f3129 | |||
| 4eda8efd42 | |||
| 7cefb52fda | |||
| a065e4b9aa | |||
| 55ee8b2917 | |||
| 0448260ed7 | |||
| 8ee8710c02 | |||
| 715b88c4cb | |||
| d81cdb8e1c | |||
| b59df66709 | |||
| c3762fb2c3 | |||
| 2a791ea676 | |||
| f23b6ba652 | |||
| b04ed5956e | |||
| c5e9d1adae | |||
| 2c2b1405a3 | |||
| 003745432f | |||
| 0e60b26513 | |||
| 6fd9edc50f | |||
| 17b89b2a35 | |||
| 0831b43654 | |||
| 16ea72e6b9 | |||
| 900c406541 | |||
| 235f2cc73c | |||
| 39954cc6af | |||
| 0bc30d382d | |||
| 2186ec7665 | |||
| aea846ad96 | |||
| 2af63c2bf1 | |||
| 7763a86634 | |||
| bb3a416320 | |||
| ba39bb8da8 | |||
| 7faf8cd06e | |||
| 913a2dbdaa | |||
| 31aa69f556 | |||
| 0d60c0e8c4 | |||
| 03aa69da99 | |||
| 78815133df | |||
| 01f773b803 | |||
| 67fe820c75 | |||
| bfc4eb31cb | |||
| 5e74547b4f | |||
| fe4555ec74 | |||
| 9edcd9b4ef | |||
| 17eef4d8a9 | |||
| 42a2454f1a | |||
| e60535d3ce | |||
| b87a8a5719 | |||
| e4cb8a6f09 | |||
| 2055bcc807 | |||
| 3b669149d2 | |||
| aaabb07bee | |||
| 7b05d52f7b | |||
| 0e64e68d31 | |||
| dcd8589b67 | |||
| ee5b5a6b2f | |||
| fa4fd48072 | |||
| 06b0fe35d2 | |||
| 65be9e1978 | |||
| 833832ac91 | |||
| e00d0528a4 | |||
| b683e68223 | |||
| 256b6bb3db | |||
| 925594d1e3 | |||
| dd81c29834 | |||
| dea5eaca30 | |||
| 5950431e73 | |||
| 05387b3921 | |||
| 986d57c7b2 | |||
| fe2416575b | |||
| ea49d7b7c5 | |||
| b92bbffa1a | |||
| f77e9bc995 | |||
| 8a16997b1e | |||
| 374c5ce471 | |||
| 58f19b4f4c | |||
| 40d61e5c5b | |||
| 2a5a143f0f | |||
| f71f76add9 | |||
| 96f1ef1347 | |||
| 4f6238aa04 | |||
| 70e6391f38 | |||
| bf62be7f63 | |||
| bbb785e43c | |||
| 369bfbfd23 | |||
| 04573615c5 | |||
| 14ae679971 | |||
| 7885ef2ac8 | |||
| a740a4ca38 | |||
| 39874d7254 | |||
| 21fff4dfb6 | |||
| 11cd884c7c | |||
| bfe4e1fed4 | |||
| cdc7f10767 | |||
| e04d663acd | |||
| 8f5ea7deda | |||
| 9405c6d206 | |||
| d6bc6895a6 | |||
| 2492c299f3 | |||
| 930611dfa8 | |||
| 4c2858fd61 | |||
| c5c3ec46ad | |||
| 62e9bed17a | |||
| 75f28245a8 | |||
| c757d2863e | |||
| 133b91a026 | |||
| 26be2402a3 | |||
| 6ffab80169 | |||
| f3eff00bf7 | |||
| 21be2aa677 | |||
| 3db496f677 | |||
| 11c2ffaf1c | |||
| 41d55c5095 | |||
| 3fe18392ef | |||
| 56a0a50da3 | |||
| c744eff30d | |||
| a7e80f7c10 | |||
| c4d4248e49 | |||
| 96aaac186d | |||
| d363e2789f | |||
| 40dc0bcd63 | |||
| 8952863b9c | |||
| cbda9b332b | |||
| fe21487f91 | |||
| 7ccdf801dd | |||
| 566630595d | |||
| 4c095a76c0 | |||
| a4c70aebd0 | |||
| 7ffa49e02c | |||
| 8eade61ec7 | |||
| 0f5aaade61 | |||
| b4f22803cd | |||
| f1c8094f5f | |||
| b0519368ce | |||
| ad7cf58450 | |||
| fc8f1890c0 | |||
| 35c7216846 | |||
| dd54f7a22a | |||
| 636ab095b0 | |||
| 2922738b6d | |||
| dd304dbe05 | |||
| a8a1838615 | |||
| cea0e94376 | |||
| ebf13b39e5 | |||
| bacc210606 | |||
| b993b9b7c7 | |||
| 2be4f2c4dc | |||
| 0d10eb5173 | |||
| 7ce6895a2c | |||
| 62c17dc3cf | |||
| 3d52661400 | |||
| 20036b82d3 | |||
| a07d8a89b6 | |||
| 10787c68d0 | |||
| 9e054d3880 | |||
| 26ccc09c46 | |||
| b37f0c5ac2 | |||
| 2d05db6f22 | |||
| 9dc36a1ece | |||
| 1fc60e6d64 | |||
| d873fe66ab | |||
| 1c728258e0 | |||
| 6b432b4011 | |||
| c90dee668f | |||
| 85321807dd | |||
| c145f28c71 | |||
| 4d3cef35c2 | |||
| ab9bbf645c | |||
| 550ca10ec6 | |||
| 21e698053f | |||
| 0330934c8b | |||
| 3cee54cd27 | |||
| 4c0d833dec | |||
| 8ba95cd498 | |||
| 87bb74312b | |||
| 4e31eef77d | |||
| a2af1d4f71 | |||
| c1ade083d9 | |||
| c839a2ccb1 | |||
| ccc8767b55 | |||
| 6c07b446f8 | |||
| b9c2d88e69 | |||
| e0fe3cce71 | |||
| fce7469961 | |||
| 9cd42650dc | |||
| 4065778255 | |||
| 0f1a56da7c | |||
| ff31d069d0 | |||
| 8f19363c07 | |||
| 0a63ab36bb | |||
| 2561c59698 | |||
| 24e8cd1a78 | |||
| bf62dca45a | |||
| bf5b7843e8 | |||
| 62fab57c3e | |||
| 9620cb550d | |||
| 9210cefb36 | |||
| 4d62f1de1e | |||
| 8b455e8bb3 | |||
| fd93ae08b6 | |||
| ce50df564c | |||
| bd786f3bea | |||
| 731b5b206f | |||
| 739f87da6d | |||
| bd5a6f8af2 | |||
| e5d9b608c4 | |||
| 1f86d8564f | |||
| d15d9f558d | |||
| d92d82d194 | |||
| e93aa158cf | |||
| 297ba85323 | |||
| ef033be59d | |||
| 36e8017580 | |||
| 4aa51f6a32 | |||
| a973d93dcb | |||
| 2409924dbc | |||
| 8bfc3845e2 | |||
| 63d3fdcf58 | |||
| 8bee226d79 | |||
| c049275512 | |||
| bbdd8ba078 | |||
| 4f31759965 | |||
| 45e866f0e1 | |||
| 6ab10fc3ac | |||
| dd576c89a3 | |||
| 89088937a7 | |||
| 6a856d677c | |||
| 94d63d4281 | |||
| 0ae297ad95 | |||
| c75b589f89 | |||
| b67382cbeb | |||
| 98b8c32f30 | |||
| 507071cc6f | |||
| 1c53595817 | |||
| a7de4aca6a | |||
| 3e0b63f65b | |||
| f34b2f7b8d | |||
| b2524f4571 | |||
| 7cc97fd96c | |||
| de52887c49 | |||
| 106413a3ea | |||
| 75a489b75b | |||
| 4709b9d2d8 | |||
| dd3655f6a6 | |||
| 35f66340d7 | |||
| f109c0134f | |||
| 005c4777c7 | |||
| 84a0a91d16 | |||
| 13540bf7f4 | |||
| 49e16a3c9f | |||
| e7cad594c1 | |||
| 46af07575e | |||
| 30d26acee0 | |||
| e72d353b45 | |||
| c4ae5c0ee5 | |||
| d0c64fca2b | |||
| cf9a01970f | |||
| 2271b9af1b | |||
| b45e784beb | |||
| c5eb3dfecb | |||
| 69df0eee77 | |||
| da94d85789 | |||
| f82d64c6e5 | |||
| 1e6ce1d2f8 | |||
| 17e5e4cd5a | |||
| f70cc29edb | |||
| 1857aa22b3 | |||
| b069646494 | |||
| 9ac34bd2c4 | |||
| a321cce5e4 | |||
| de7018539d | |||
| 874fb7ae78 | |||
| 94521629ab | |||
| 03c4c37969 | |||
| eae1ebfd29 | |||
| bcac7bd6d2 | |||
| 0b3d13645f | |||
| 5ebf066dd5 | |||
| a56bd1fbba | |||
| ba104b61bf | |||
| 8abd163464 | |||
| e5dbd2c3a5 | |||
| a57d144076 | |||
| 2918c3d75a | |||
| 29680100ac | |||
| 8bce760174 | |||
| b3d128bb39 | |||
| 741e51396b | |||
| 00f3ad7217 | |||
| cdcf44b3ef | |||
| 1389cc9600 | |||
| e77a5d5f13 | |||
| 3fbe1f8d64 | |||
| 11f3c0741e | |||
| 0b31eca9c2 | |||
| d9231afa29 | |||
| 9eecb5a9fe | |||
| c370cc10e9 | |||
| 05476d6604 | |||
| 9a29fc2ce1 | |||
| c1007c7276 | |||
| dcdd5d64b6 | |||
| 9333f82be0 | |||
| 246d3761ce | |||
| 27cf6e549e | |||
| a8763c1fec | |||
| a83a1cafa7 | |||
| 7295dd7dec | |||
| 3bf5d34544 | |||
| b6e8de23f8 | |||
| 73e9c44377 | |||
| 661f3e2160 | |||
| 6fcb07d41e | |||
| 54f7ebdec9 | |||
| 873b2cabb8 | |||
| c5781afa47 | |||
| 171e705ba4 | |||
| 3ad6d13ff3 | |||
| 207218e920 | |||
| 31cc519cd3 | |||
| 3c15913f53 | |||
| 502aa1f053 | |||
| fdda19cb0c | |||
| 5ea5d08892 | |||
| 95468b72f3 | |||
| debc1c4c95 | |||
| be7d4608fb | |||
| 3c5d87cbae | |||
| 08c6ffaa8d | |||
| 2a30d8c9f9 | |||
| da0188acc0 | |||
| db52841544 | |||
| 8c7d29e526 | |||
| 94c58e7347 | |||
| 583e3a6875 | |||
| e4377e7c51 | |||
| 0a1767a6b5 | |||
| 8ef23d64a1 | |||
| e685dcef0b | |||
| 24efb02d93 | |||
| e832870288 | |||
| 195c12f2db | |||
| cd7f0a5177 | |||
| 6b1d9971f4 | |||
| f9f013e264 | |||
| 9d593e7779 | |||
| bfd800342b | |||
| 6fe1898ab6 | |||
| 990e87effb | |||
| 55ff28ed93 | |||
| 2375e58705 | |||
| 50bda8adfe | |||
| 8a679128fa | |||
| f743c82bdd | |||
| 3e2b05b096 | |||
| aa5204958e | |||
| 459a2bbc9e | |||
| 8c93b61e2c | |||
| 92c4b6ae4c | |||
| eb6279d6ff | |||
| 5d03262b00 | |||
| 6cf4371eb4 | |||
| ba8a6e3533 | |||
| ec38ef9b10 | |||
| 10651d491f | |||
| 6f39f9a6a0 | |||
| 931b32d102 | |||
| 89ce2dc405 | |||
| 27cfe31b64 | |||
| 9a92777fc1 | |||
| db7922b9ee | |||
| 5d31d6ebbb | |||
| 2d20aa4ed0 | |||
| 74abbd0898 | |||
| aa1b031274 | |||
| 8e8da3f5b0 | |||
| d00561d396 | |||
| b46364e436 | |||
| e0ee2f769a | |||
| e65c6270bf | |||
| 147f3ebf0a | |||
| 53e685461e | |||
| 11b24eb49f | |||
| 92a3dbe18f | |||
| f46fa6e096 | |||
| 408ef5c65b | |||
| 38abc61de5 | |||
| c7049ca627 | |||
| 5f990566c4 | |||
| ddfe688be6 | |||
| bc7f6fc44c | |||
| da0b1d8821 | |||
| 69f1d1ddff | |||
| 47c4dcc8a3 | |||
| c321d025c4 | |||
| 34c70e7a1c | |||
| 0c1bcf354c | |||
| 1de10f9f86 | |||
| d8425d8881 | |||
| 649bb7ac04 | |||
| ebdd74105a | |||
| b28fd79fdc | |||
| 78784040aa | |||
| 2570a5af38 | |||
| 17ece5861a | |||
| 6e12391c91 | |||
| 5169c79978 | |||
| 220d1f0b3e | |||
| ed9c924102 | |||
| 710506e9e2 | |||
| c4c1c4c9bb | |||
| 61404dd2ac | |||
| 276e47afae | |||
| d69b3e385e | |||
| 0d7666a012 | |||
| 0aadab0941 | |||
| dcbed8d676 | |||
| eee638fd81 | |||
| c624d82383 | |||
| 0c00242f7a | |||
| 12d7429ff0 | |||
| 822c67fdee | |||
| 56cedfc4d2 | |||
| e03c81d90a | |||
| aec59aba34 | |||
| 28cfb07445 | |||
| 9cb13435a1 | |||
| 29eda007bb | |||
| ee9c0f5f9c | |||
| a9e33fe54e | |||
| e192c6842e | |||
| 595437bdd1 | |||
| 14451f3f06 | |||
| 9fbce6c8c8 | |||
| a41d877fe6 | |||
| 88a338a3eb | |||
| d7240d1a28 | |||
| 9a4b5a4545 | |||
| ec7f74f7b4 | |||
| afbcc07184 | |||
| 91a0270432 | |||
| eaba0e6358 | |||
| f1c8e04268 | |||
| a78d143c98 | |||
| 789dc0a3b7 | |||
| 526220a171 | |||
| 5bfaf9931b | |||
| 007354ebaf | |||
| 232d6b87f4 | |||
| b1851e2f16 | |||
| 64cf206fb5 | |||
| b2da9df82d | |||
| 8638bba670 | |||
| e827a5bd9e | |||
| 9053839282 | |||
| 3240f2a6b7 | |||
| 57ed0e57f0 | |||
| ca0b6fbb95 | |||
| 0af3638c5a | |||
| f710d96602 | |||
| bc8a91ed83 | |||
| bb8be523c5 | |||
| 236815ec40 | |||
| e720427b53 | |||
| a8a208e0fe | |||
| 3fbaabc866 | |||
| 5e4dfb9546 | |||
| 13b0fd993c | |||
| 05248a3dea | |||
| 46fe74177d | |||
| df86f0752a | |||
| 4c7aa8645a | |||
| 1304ad1e12 | |||
| e16227b53c | |||
| 47ae5f14f5 | |||
| 156d736e75 | |||
| b7945827f9 | |||
| 1e11657ba4 | |||
| ca5e0f53b8 | |||
| bbe0f2cef5 | |||
| c2a880d7c9 | |||
| 9552206a4c | |||
| 2722133206 | |||
| b76e88354c | |||
| 88e54ca1c0 | |||
| 642e4d97a4 | |||
| 16a9407fbf | |||
| e19000a56f | |||
| 7521bcc32c | |||
| d3a15c625a | |||
| af746a9269 | |||
| 37be9ddeec | |||
| 28f9ce4e33 | |||
| 8633ef451e | |||
| a347569350 | |||
| 1b842c4762 | |||
| 7c3b415d6a | |||
| 03fb85f179 | |||
| 9ac9e9e29a | |||
| 0689b43a7c | |||
| 3450528fad | |||
| fb3298f076 | |||
| e9017fb71e | |||
| 476d2e1eea | |||
| 4950f542d1 | |||
| 22d8a81a57 | |||
| 976ee44682 | |||
| 3b5f1f767d | |||
| b3c1bd788a | |||
| fa4d32f7dd | |||
| 91e06e7c05 | |||
| 072f873df6 | |||
| 88f5e2f3f7 | |||
| f0637e5e91 | |||
| 519e452e1a | |||
| 89eca83f5b | |||
| 48f7cbec75 | |||
| bfb18d29a6 | |||
| 678e114e5f | |||
| 18dbed0e0d | |||
| 488eb11ba8 | |||
| b6561ce57d | |||
| a2fed4c790 | |||
| 9a1835ce66 | |||
| ece3fac7ef | |||
| 607ff2e189 | |||
| a47b7a34be | |||
| 3476440ec3 | |||
| b333b1f6e8 | |||
| 6501d54267 | |||
| 9228a8bee6 | |||
| f3cb5084cf | |||
| dfb348ef0b | |||
| 50d350bcd1 | |||
| 0aab7c6fc3 | |||
| 13dffa0017 | |||
| b417b4dbee | |||
| fd711219a2 | |||
| a789c08588 | |||
| 75533fcd06 | |||
| 82902b6fd5 | |||
| 124c4825a7 | |||
| 11470077e1 | |||
| 67e24dd347 | |||
| 519fbdb8ab | |||
| d22df8c41f | |||
| a8aff6f643 | |||
| 48e2d38be7 | |||
| 4ee25c7e95 | |||
| 9408a5ef5d | |||
| 67f163892e | |||
| d0bdf99501 | |||
| 156871d888 | |||
| eb768514c3 | |||
| ec47a0a6de | |||
| 74defefd61 | |||
| 4e0351bafd | |||
| 820fdbff23 | |||
| 776c8c5c87 | |||
| c1dec72fb6 | |||
| aa5caf83f6 | |||
| 993ff7ad12 | |||
| 3413c23b34 | |||
| 383559c228 | |||
| 07979b02c3 | |||
| ddca982c09 | |||
| 6f848b8520 | |||
| 9e9fd281b9 | |||
| 5c13ffb73e | |||
| a446cb271f | |||
| 6eed6a8e9f | |||
| 576d43d1c2 | |||
| 3e708e015c | |||
| c6c519166e | |||
| 2bac66a181 | |||
| ff5eaadfdf | |||
| c10283b2b6 | |||
| b34ebc8cfe | |||
| a50efda49d | |||
| 4b7c0b9c76 | |||
| 68e50661a2 | |||
| f1adc32765 | |||
| a5d16f09e6 | |||
| 36b34465a4 | |||
| 9bb5d6cab0 | |||
| 205ccddff8 | |||
| 533d399dab | |||
| 1d01e435ca | |||
| c659f94dca | |||
| dc1502ad71 | |||
| bb5c40d944 | |||
| e71ff78f7f | |||
| 062d2179eb | |||
| 73b5ef48ef | |||
| a46adbfcbc | |||
| c25f587584 | |||
| e3ec3566d9 | |||
| f9828cd0c9 | |||
| 203b398caa | |||
| a22f03e749 | |||
| 8f96b15e2a | |||
| 7c91700cc6 | |||
| 6f93a3ac5b | |||
| 85ef02bc5c | |||
| 66cdc10010 | |||
| a2b6b595c2 | |||
| 474e5f4cdf | |||
| 00d2e279d7 | |||
| e357e2c1b7 | |||
| 1989d94ef5 | |||
| 896c34fab3 | |||
| 3668a01fca | |||
| 2b44c0b649 | |||
| 89a740a62f | |||
| a8e5d1d9fd | |||
| 256aa53326 | |||
| 5b363df2ae | |||
| 10e639cdb9 | |||
| 8c66531c42 | |||
| b7630a088b | |||
| c9cedf3c5b | |||
| f94e64a0ae | |||
| 8b22099da2 | |||
| 0c8e069640 | |||
| 587e9a554e | |||
| 7e99911c82 | |||
| ec5038f2a7 | |||
| 97f5d05d1f | |||
| f4fdd94cf5 | |||
| 7cc0b0f93e | |||
| e16e141c38 | |||
| 65598e4075 | |||
| b345c6e552 | |||
| c0cde75d54 | |||
| 85474f581d | |||
| e6d7989b20 | |||
| 406fe899d5 | |||
| b3c77e664d | |||
| 5e08d588f8 | |||
| e6f27240d5 | |||
| 46fa9a6bd4 | |||
| f1aae52daf | |||
| 45f0cf0d41 | |||
| a891caa755 | |||
| d33d37ffd9 | |||
| 1c3b9d7591 | |||
| 9be630466f | |||
| fa36b9d345 | |||
| 3650ec02be | |||
| a85b4b5805 | |||
| c3a8db6d6c | |||
| 23e53a32e5 | |||
| d1f727191f | |||
| 579ffbf97e | |||
| 3153450756 | |||
| 8a6fabed83 | |||
| c6f666a02b | |||
| 5fad2a3f24 | |||
| d015b55e6b | |||
| a2f3692bff | |||
| b7a053b0e5 | |||
| 7dd3723abe | |||
| 70bbf17b13 | |||
| 0bd68671bd | |||
| d0f883524e | |||
| ad74fdd7cc | |||
| ce06fbdf77 | |||
| 090c46eca2 | |||
| c3e409f950 | |||
| dc31ddc80a | |||
| ca6beb9ca8 | |||
| be7d060ea4 | |||
| fdbc598e4c | |||
| 40686b5e87 | |||
| b3900c5dfe | |||
| 7a95e654eb | |||
| 05d187ec90 | |||
| 8415b907bb | |||
| f92c9ddb43 | |||
| 1f724e2e8a | |||
| 5ad02d7a9a | |||
| d8fdf9321d | |||
| 4826d976d6 | |||
| 61b9484155 | |||
| 89a0a46a69 | |||
| 4b7fc59332 | |||
| 5b23c0b176 | |||
| 0bc9a0db18 | |||
| 3f3e6ba739 |
@@ -31,6 +31,8 @@
|
||||
*.py text
|
||||
*.qrc text
|
||||
*.qss text
|
||||
*.rc text
|
||||
*.rc.in text
|
||||
*.S text
|
||||
*.sbt text
|
||||
*.scala text
|
||||
|
||||
+1
-2
@@ -17,9 +17,8 @@ bin/
|
||||
*.rule
|
||||
*.tmp
|
||||
*/debug
|
||||
*/CMakeFiles
|
||||
CMakeCache.txt
|
||||
*.suo
|
||||
*.log
|
||||
*.tlog
|
||||
build
|
||||
.cache
|
||||
|
||||
Vendored
+1
-1
@@ -67,7 +67,7 @@ function(compile_carotene)
|
||||
endif()
|
||||
check_cxx_compiler_flag("-mfpu=neon" CXX_HAS_MFPU_NEON)
|
||||
check_c_compiler_flag("-mfpu=neon" C_HAS_MFPU_NEON)
|
||||
if(${CXX_HAS_MFPU_NEON} AND ${C_HAS_MFPU_NEON})
|
||||
if(${CXX_HAS_MFPU_NEON} AND ${C_HAS_MFPU_NEON} AND NOT "${CMAKE_CXX_FLAGS} " MATCHES "-mfpu=neon[^ ]*")
|
||||
get_target_property(old_flags "carotene_objs" COMPILE_FLAGS)
|
||||
if(old_flags)
|
||||
set_target_properties("carotene_objs" PROPERTIES COMPILE_FLAGS "${old_flags} -mfpu=neon")
|
||||
|
||||
+4
-4
@@ -231,7 +231,7 @@ void extract4(const Size2D &size,
|
||||
srcStride == dst2Stride && \
|
||||
srcStride == dst3Stride &&
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
|
||||
#define SPLIT_ASM2(sgn, bits) __asm__ ( \
|
||||
"vld2." #bits " {d0, d2}, [%[in0]] \n\t" \
|
||||
@@ -280,7 +280,7 @@ void extract4(const Size2D &size,
|
||||
FILL_LINES##n(VST1Q, sgn##bits) \
|
||||
}
|
||||
|
||||
#endif // __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#endif
|
||||
|
||||
#define SPLIT(sgn,bits,n) void split##n(const Size2D &_size, \
|
||||
const sgn##bits * srcBase, ptrdiff_t srcStride \
|
||||
@@ -351,7 +351,7 @@ void extract4(const Size2D &size,
|
||||
} \
|
||||
}
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
|
||||
#define ALPHA_QUAD(sgn, bits) { \
|
||||
internal::prefetch(src + sj); \
|
||||
@@ -378,7 +378,7 @@ void extract4(const Size2D &size,
|
||||
vst1q_##sgn##bits(dst1 + d1j, vals.v4.val[3]); \
|
||||
}
|
||||
|
||||
#endif // __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#endif
|
||||
|
||||
#define SPLIT4ALPHA(sgn,bits) void split4(const Size2D &_size, \
|
||||
const sgn##bits * srcBase, ptrdiff_t srcStride, \
|
||||
|
||||
+2
-2
@@ -77,7 +77,7 @@ namespace CAROTENE_NS {
|
||||
dstStride == src2Stride && \
|
||||
dstStride == src3Stride &&
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
|
||||
#define MERGE_ASM2(sgn, bits) __asm__ ( \
|
||||
"vld1." #bits " {d0-d1}, [%[in0]] \n\t" \
|
||||
@@ -128,7 +128,7 @@ namespace CAROTENE_NS {
|
||||
vst##n##q_##sgn##bits(dst + dj, v_dst); \
|
||||
}
|
||||
|
||||
#endif // __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#endif
|
||||
|
||||
#define COMBINE(sgn,bits,n) void combine##n(const Size2D &_size \
|
||||
FILL_LINES##n(FARG, sgn##bits), \
|
||||
|
||||
+41
-41
@@ -97,7 +97,7 @@ void rgb2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
const u32 G2Y = color_space == COLOR_SPACE_BT601 ? G2Y_BT601 : G2Y_BT709;
|
||||
const u32 B2Y = color_space == COLOR_SPACE_BT601 ? B2Y_BT601 : B2Y_BT709;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register int16x4_t v_r2y asm ("d31") = vmov_n_s16(R2Y);
|
||||
register int16x4_t v_g2y asm ("d30") = vmov_n_s16(G2Y);
|
||||
register int16x4_t v_b2y asm ("d29") = vmov_n_s16(B2Y);
|
||||
@@ -116,7 +116,7 @@ void rgb2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
u8 * dst = internal::getRowPtr(dstBase, dstStride, i);
|
||||
size_t sj = 0u, dj = 0u;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
for (; dj < roiw8; sj += 24, dj += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
@@ -198,7 +198,7 @@ void rgbx2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
const u32 G2Y = color_space == COLOR_SPACE_BT601 ? G2Y_BT601 : G2Y_BT709;
|
||||
const u32 B2Y = color_space == COLOR_SPACE_BT601 ? B2Y_BT601 : B2Y_BT709;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register int16x4_t v_r2y asm ("d31") = vmov_n_s16(R2Y);
|
||||
register int16x4_t v_g2y asm ("d30") = vmov_n_s16(G2Y);
|
||||
register int16x4_t v_b2y asm ("d29") = vmov_n_s16(B2Y);
|
||||
@@ -217,7 +217,7 @@ void rgbx2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
u8 * dst = internal::getRowPtr(dstBase, dstStride, i);
|
||||
size_t sj = 0u, dj = 0u;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
for (; dj < roiw8; sj += 32, dj += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
@@ -300,7 +300,7 @@ void bgr2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
const u32 G2Y = color_space == COLOR_SPACE_BT601 ? G2Y_BT601 : G2Y_BT709;
|
||||
const u32 B2Y = color_space == COLOR_SPACE_BT601 ? B2Y_BT601 : B2Y_BT709;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register int16x4_t v_r2y asm ("d31") = vmov_n_s16(R2Y);
|
||||
register int16x4_t v_g2y asm ("d30") = vmov_n_s16(G2Y);
|
||||
register int16x4_t v_b2y asm ("d29") = vmov_n_s16(B2Y);
|
||||
@@ -319,7 +319,7 @@ void bgr2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
u8 * dst = internal::getRowPtr(dstBase, dstStride, i);
|
||||
size_t sj = 0u, dj = 0u;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
for (; dj < roiw8; sj += 24, dj += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
@@ -402,7 +402,7 @@ void bgrx2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
const u32 G2Y = color_space == COLOR_SPACE_BT601 ? G2Y_BT601 : G2Y_BT709;
|
||||
const u32 B2Y = color_space == COLOR_SPACE_BT601 ? B2Y_BT601 : B2Y_BT709;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register int16x4_t v_r2y asm ("d31") = vmov_n_s16(R2Y);
|
||||
register int16x4_t v_g2y asm ("d30") = vmov_n_s16(G2Y);
|
||||
register int16x4_t v_b2y asm ("d29") = vmov_n_s16(B2Y);
|
||||
@@ -421,7 +421,7 @@ void bgrx2gray(const Size2D &size, COLOR_SPACE color_space,
|
||||
u8 * dst = internal::getRowPtr(dstBase, dstStride, i);
|
||||
size_t sj = 0u, dj = 0u;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
for (; dj < roiw8; sj += 32, dj += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
@@ -512,7 +512,7 @@ void gray2rgb(const Size2D &size,
|
||||
for (; sj < roiw16; sj += 16, dj += 48)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld1.8 {d0-d1}, [%[in0]] \n\t"
|
||||
"vmov.8 q1, q0 \n\t"
|
||||
@@ -538,7 +538,7 @@ void gray2rgb(const Size2D &size,
|
||||
|
||||
if (sj < roiw8)
|
||||
{
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld1.8 {d0}, [%[in]] \n\t"
|
||||
"vmov.8 d1, d0 \n\t"
|
||||
@@ -584,7 +584,7 @@ void gray2rgbx(const Size2D &size,
|
||||
size_t roiw16 = size.width >= 15 ? size.width - 15 : 0;
|
||||
size_t roiw8 = size.width >= 7 ? size.width - 7 : 0;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register uint8x16_t vc255 asm ("q4") = vmovq_n_u8(255);
|
||||
#else
|
||||
uint8x16x4_t vRgba;
|
||||
@@ -602,7 +602,7 @@ void gray2rgbx(const Size2D &size,
|
||||
for (; sj < roiw16; sj += 16, dj += 64)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld1.8 {d0-d1}, [%[in0]] \n\t"
|
||||
"vmov.8 q1, q0 \n\t"
|
||||
@@ -628,7 +628,7 @@ void gray2rgbx(const Size2D &size,
|
||||
|
||||
if (sj < roiw8)
|
||||
{
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld1.8 {d5}, [%[in]] \n\t"
|
||||
"vmov.8 d6, d5 \n\t"
|
||||
@@ -1409,7 +1409,7 @@ inline void convertToHSV(const s32 r, const s32 g, const s32 b,
|
||||
"d24","d25","d26","d27","d28","d29","d30","d31" \
|
||||
);
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
|
||||
#define YCRCB_CONSTS \
|
||||
register int16x4_t vcYR asm ("d31") = vmov_n_s16(4899); \
|
||||
@@ -1555,7 +1555,7 @@ inline uint8x8x3_t convertToYCrCb( const int16x8_t& vR, const int16x8_t& vG, con
|
||||
#define COEFF_G ( 8663)
|
||||
#define COEFF_B (-17705)
|
||||
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
#define YUV420ALPHA3_CONST
|
||||
#define YUV420ALPHA4_CONST register uint8x16_t c255 asm ("q13") = vmovq_n_u8(255);
|
||||
#define YUV420ALPHA3_CONVERT
|
||||
@@ -1852,7 +1852,7 @@ void rgb2hsv(const Size2D &size,
|
||||
#ifdef CAROTENE_NEON
|
||||
size_t roiw8 = size.width >= 7 ? size.width - 7 : 0;
|
||||
const s32 hsv_shift = 12;
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register const f32 vsdiv_table = f32(255 << hsv_shift);
|
||||
register f32 vhdiv_table = f32(hrange << hsv_shift);
|
||||
register const s32 vhrange = hrange;
|
||||
@@ -1871,7 +1871,7 @@ void rgb2hsv(const Size2D &size,
|
||||
for (; j < roiw8; sj += 24, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERT_TO_HSV_ASM(vld3.8 {d0-d2}, d0, d2)
|
||||
#else
|
||||
uint8x8x3_t vRgb = vld3_u8(src + sj);
|
||||
@@ -1904,7 +1904,7 @@ void rgbx2hsv(const Size2D &size,
|
||||
#ifdef CAROTENE_NEON
|
||||
size_t roiw8 = size.width >= 7 ? size.width - 7 : 0;
|
||||
const s32 hsv_shift = 12;
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register const f32 vsdiv_table = f32(255 << hsv_shift);
|
||||
register f32 vhdiv_table = f32(hrange << hsv_shift);
|
||||
register const s32 vhrange = hrange;
|
||||
@@ -1923,7 +1923,7 @@ void rgbx2hsv(const Size2D &size,
|
||||
for (; j < roiw8; sj += 32, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERT_TO_HSV_ASM(vld4.8 {d0-d3}, d0, d2)
|
||||
#else
|
||||
uint8x8x4_t vRgb = vld4_u8(src + sj);
|
||||
@@ -1956,7 +1956,7 @@ void bgr2hsv(const Size2D &size,
|
||||
#ifdef CAROTENE_NEON
|
||||
size_t roiw8 = size.width >= 7 ? size.width - 7 : 0;
|
||||
const s32 hsv_shift = 12;
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register const f32 vsdiv_table = f32(255 << hsv_shift);
|
||||
register f32 vhdiv_table = f32(hrange << hsv_shift);
|
||||
register const s32 vhrange = hrange;
|
||||
@@ -1975,7 +1975,7 @@ void bgr2hsv(const Size2D &size,
|
||||
for (; j < roiw8; sj += 24, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERT_TO_HSV_ASM(vld3.8 {d0-d2}, d2, d0)
|
||||
#else
|
||||
uint8x8x3_t vRgb = vld3_u8(src + sj);
|
||||
@@ -2008,7 +2008,7 @@ void bgrx2hsv(const Size2D &size,
|
||||
#ifdef CAROTENE_NEON
|
||||
size_t roiw8 = size.width >= 7 ? size.width - 7 : 0;
|
||||
const s32 hsv_shift = 12;
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register const f32 vsdiv_table = f32(255 << hsv_shift);
|
||||
register f32 vhdiv_table = f32(hrange << hsv_shift);
|
||||
register const s32 vhrange = hrange;
|
||||
@@ -2027,7 +2027,7 @@ void bgrx2hsv(const Size2D &size,
|
||||
for (; j < roiw8; sj += 32, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERT_TO_HSV_ASM(vld4.8 {d0-d3}, d2, d0)
|
||||
#else
|
||||
uint8x8x4_t vRgb = vld4_u8(src + sj);
|
||||
@@ -2068,7 +2068,7 @@ void rgbx2bgr565(const Size2D &size,
|
||||
for (; j < roiw16; sj += 64, dj += 32, j += 16)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld4.8 {d2, d4, d6, d8}, [%[in0]] @ q0 q1 q2 q3 q4 \n\t"
|
||||
"vld4.8 {d3, d5, d7, d9}, [%[in1]] @ xxxxxxxx rrrrRRRR ggggGGGG bbbbBBBB xxxxxxxx \n\t"
|
||||
@@ -2122,7 +2122,7 @@ void rgb2bgr565(const Size2D &size,
|
||||
for (; j < roiw16; sj += 48, dj += 32, j += 16)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld3.8 {d2, d4, d6}, [%[in0]] @ q0 q1 q2 q3 q4 \n\t"
|
||||
"vld3.8 {d3, d5, d7}, [%[in1]] @ xxxxxxxx rrrrRRRR ggggGGGG bbbbBBBB xxxxxxxx \n\t"
|
||||
@@ -2176,7 +2176,7 @@ void rgbx2rgb565(const Size2D &size,
|
||||
for (; j < roiw16; sj += 64, dj += 32, j += 16)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld4.8 {d0, d2, d4, d6}, [%[in0]] @ q0 q1 q2 q3 \n\t"
|
||||
"vld4.8 {d1, d3, d5, d7}, [%[in1]] @ rrrrRRRR ggggGGGG bbbbBBBB aaaaAAAA \n\t"
|
||||
@@ -2230,7 +2230,7 @@ void rgb2rgb565(const Size2D &size,
|
||||
for (; j < roiw16; sj += 48, dj += 32, j += 16)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld3.8 {d0, d2, d4}, [%[in0]] @ q0 q1 q2 q3 \n\t"
|
||||
"vld3.8 {d1, d3, d5}, [%[in1]] @ rrrrRRRR ggggGGGG bbbbBBBB xxxxxxxx \n\t"
|
||||
@@ -2285,7 +2285,7 @@ void rgb2ycrcb(const Size2D &size,
|
||||
for (; j < roiw8; sj += 24, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTTOYCRCB(vld3.8 {d0-d2}, d0, d1, d2)
|
||||
#else
|
||||
uint8x8x3_t vRgb = vld3_u8(src + sj);
|
||||
@@ -2329,7 +2329,7 @@ void rgbx2ycrcb(const Size2D &size,
|
||||
for (; j < roiw8; sj += 32, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTTOYCRCB(vld4.8 {d0-d3}, d0, d1, d2)
|
||||
#else
|
||||
uint8x8x4_t vRgba = vld4_u8(src + sj);
|
||||
@@ -2373,7 +2373,7 @@ void bgr2ycrcb(const Size2D &size,
|
||||
for (; j < roiw8; sj += 24, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTTOYCRCB(vld3.8 {d0-d2}, d2, d1, d0)
|
||||
#else
|
||||
uint8x8x3_t vBgr = vld3_u8(src + sj);
|
||||
@@ -2417,7 +2417,7 @@ void bgrx2ycrcb(const Size2D &size,
|
||||
for (; j < roiw8; sj += 32, dj += 24, j += 8)
|
||||
{
|
||||
internal::prefetch(src + sj);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTTOYCRCB(vld4.8 {d0-d3}, d2, d1, d0)
|
||||
#else
|
||||
uint8x8x4_t vBgra = vld4_u8(src + sj);
|
||||
@@ -2466,9 +2466,9 @@ void yuv420sp2rgb(const Size2D &size,
|
||||
// B = [((149*y)/2 + (-17705+129*u) )/2]/32
|
||||
|
||||
// error estimation:
|
||||
//Rerr = 0.0000625 * y − 0.00225 * v − 0.287
|
||||
//Rerr = 0.0000625 * y - 0.00225 * v - 0.287
|
||||
//Gerr = 0.0000625 * y + 0.0005 * v + 0.000375 * u + 0.128625
|
||||
//Berr = 0.0000625 * y − 0.002375 * u - 0.287375
|
||||
//Berr = 0.0000625 * y - 0.002375 * u - 0.287375
|
||||
|
||||
//real error test:
|
||||
//=================
|
||||
@@ -2499,7 +2499,7 @@ void yuv420sp2rgb(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(3, d1, d0, q5, q6)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2545,7 +2545,7 @@ void yuv420sp2rgbx(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(4, d1, d0, q5, q6)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2591,7 +2591,7 @@ void yuv420i2rgb(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(3, d0, d1, q5, q6)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2637,7 +2637,7 @@ void yuv420i2rgbx(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(4, d0, d1, q5, q6)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2683,7 +2683,7 @@ void yuv420sp2bgr(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(3, d1, d0, q6, q5)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2729,7 +2729,7 @@ void yuv420sp2bgrx(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(4, d1, d0, q6, q5)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2775,7 +2775,7 @@ void yuv420i2bgr(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(3, d0, d1, q6, q5)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
@@ -2821,7 +2821,7 @@ void yuv420i2bgrx(const Size2D &size,
|
||||
internal::prefetch(uv + j);
|
||||
internal::prefetch(y1 + j);
|
||||
internal::prefetch(y2 + j);
|
||||
#if defined(__GNUC__) && __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CONVERTYUV420TORGB(4, d0, d1, q6, q5)
|
||||
#else
|
||||
convertYUV420.ToRGB(y1 + j, y2 + j, uv + j, dst1 + dj, dst2 + dj);
|
||||
|
||||
Vendored
+27
-27
@@ -101,7 +101,7 @@ CVT_FUNC(u8, s8, 16,
|
||||
}
|
||||
})
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(u8, u16, 16,
|
||||
register uint8x16_t zero0 asm ("q1") = vmovq_n_u8(0);,
|
||||
{
|
||||
@@ -135,7 +135,7 @@ CVT_FUNC(u8, u16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(u8, s32, 16,
|
||||
register uint8x16_t zero0 asm ("q1") = vmovq_n_u8(0);
|
||||
register uint8x16_t zero1 asm ("q2") = vmovq_n_u8(0);
|
||||
@@ -173,7 +173,7 @@ CVT_FUNC(u8, s32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(u8, f32, 16,
|
||||
,
|
||||
{
|
||||
@@ -248,7 +248,7 @@ CVT_FUNC(s8, u8, 16,
|
||||
}
|
||||
})
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(s8, u16, 16,
|
||||
register uint8x16_t zero0 asm ("q1") = vmovq_n_u8(0);,
|
||||
{
|
||||
@@ -284,7 +284,7 @@ CVT_FUNC(s8, u16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s8, s16, 16,
|
||||
,
|
||||
{
|
||||
@@ -323,7 +323,7 @@ CVT_FUNC(s8, s16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(s8, s32, 16,
|
||||
,
|
||||
{
|
||||
@@ -377,7 +377,7 @@ CVT_FUNC(s8, s32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s8, f32, 16,
|
||||
,
|
||||
{
|
||||
@@ -440,7 +440,7 @@ CVT_FUNC(s8, f32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(u16, u8, 16,
|
||||
,
|
||||
{
|
||||
@@ -479,7 +479,7 @@ CVT_FUNC(u16, u8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(u16, s8, 16,
|
||||
register uint8x16_t v127 asm ("q4") = vmovq_n_u8(127);,
|
||||
{
|
||||
@@ -522,7 +522,7 @@ CVT_FUNC(u16, s8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(u16, s16, 8,
|
||||
register uint16x8_t v32767 asm ("q4") = vmovq_n_u16(0x7FFF);,
|
||||
{
|
||||
@@ -555,7 +555,7 @@ CVT_FUNC(u16, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(u16, s32, 8,
|
||||
register uint16x8_t zero0 asm ("q1") = vmovq_n_u16(0);,
|
||||
{
|
||||
@@ -589,7 +589,7 @@ CVT_FUNC(u16, s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(u16, f32, 8,
|
||||
,
|
||||
{
|
||||
@@ -633,7 +633,7 @@ CVT_FUNC(u16, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s16, u8, 16,
|
||||
,
|
||||
{
|
||||
@@ -672,7 +672,7 @@ CVT_FUNC(s16, u8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s16, s8, 16,
|
||||
,
|
||||
{
|
||||
@@ -711,7 +711,7 @@ CVT_FUNC(s16, s8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVT_FUNC(s16, u16, 8,
|
||||
register int16x8_t vZero asm ("q4") = vmovq_n_s16(0);,
|
||||
{
|
||||
@@ -747,7 +747,7 @@ CVT_FUNC(s16, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s16, s32, 8,
|
||||
,
|
||||
{
|
||||
@@ -786,7 +786,7 @@ CVT_FUNC(s16, s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s16, f32, 8,
|
||||
,
|
||||
{
|
||||
@@ -829,7 +829,7 @@ CVT_FUNC(s16, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s32, u8, 8,
|
||||
,
|
||||
{
|
||||
@@ -870,7 +870,7 @@ CVT_FUNC(s32, u8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s32, s8, 8,
|
||||
,
|
||||
{
|
||||
@@ -911,7 +911,7 @@ CVT_FUNC(s32, s8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s32, u16, 8,
|
||||
,
|
||||
{
|
||||
@@ -950,7 +950,7 @@ CVT_FUNC(s32, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s32, s16, 8,
|
||||
,
|
||||
{
|
||||
@@ -989,7 +989,7 @@ CVT_FUNC(s32, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(s32, f32, 8,
|
||||
,
|
||||
{
|
||||
@@ -1034,7 +1034,7 @@ CVT_FUNC(s32, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(f32, u8, 8,
|
||||
register float32x4_t vmult asm ("q0") = vdupq_n_f32((float)(1 << 16));
|
||||
register uint32x4_t vmask asm ("q1") = vdupq_n_u32(1<<16);,
|
||||
@@ -1101,7 +1101,7 @@ CVT_FUNC(f32, u8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(f32, s8, 8,
|
||||
register float32x4_t vhalf asm ("q0") = vdupq_n_f32(0.5f);,
|
||||
{
|
||||
@@ -1153,7 +1153,7 @@ CVT_FUNC(f32, s8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(f32, u16, 8,
|
||||
register float32x4_t vhalf asm ("q0") = vdupq_n_f32(0.5f);,
|
||||
{
|
||||
@@ -1212,7 +1212,7 @@ CVT_FUNC(f32, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(f32, s16, 8,
|
||||
register float32x4_t vhalf asm ("q0") = vdupq_n_f32(0.5f);,
|
||||
{
|
||||
@@ -1271,7 +1271,7 @@ CVT_FUNC(f32, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
CVT_FUNC(f32, s32, 8,
|
||||
register float32x4_t vhalf asm ("q0") = vdupq_n_f32(0.5f);,
|
||||
{
|
||||
|
||||
+28
-28
@@ -473,7 +473,7 @@ CVTS_FUNC(u8, s16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u8, s32, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -562,7 +562,7 @@ CVTS_FUNC(u8, s32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u8, f32, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
@@ -985,7 +985,7 @@ CVTS_FUNC(s8, s16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s8, s32, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1074,7 +1074,7 @@ CVTS_FUNC(s8, s32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s8, f32, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
@@ -1155,7 +1155,7 @@ CVTS_FUNC(s8, f32, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u16, u8, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1214,7 +1214,7 @@ CVTS_FUNC(u16, u8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u16, s8, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1273,7 +1273,7 @@ CVTS_FUNC(u16, s8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC1(u16, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1330,7 +1330,7 @@ CVTS_FUNC1(u16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u16, s16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1387,7 +1387,7 @@ CVTS_FUNC(u16, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u16, s32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1443,7 +1443,7 @@ CVTS_FUNC(u16, s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(u16, f32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
@@ -1495,7 +1495,7 @@ CVTS_FUNC(u16, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s16, u8, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1554,7 +1554,7 @@ CVTS_FUNC(s16, u8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s16, s8, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1613,7 +1613,7 @@ CVTS_FUNC(s16, s8, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s16, u16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1670,7 +1670,7 @@ CVTS_FUNC(s16, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC1(s16, 16,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1727,7 +1727,7 @@ CVTS_FUNC1(s16, 16,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s16, s32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1783,7 +1783,7 @@ CVTS_FUNC(s16, s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s16, f32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
@@ -1835,7 +1835,7 @@ CVTS_FUNC(s16, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s32, u8, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1893,7 +1893,7 @@ CVTS_FUNC(s32, u8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s32, s8, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -1951,7 +1951,7 @@ CVTS_FUNC(s32, s8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s32, u16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2007,7 +2007,7 @@ CVTS_FUNC(s32, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s32, s16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2063,7 +2063,7 @@ CVTS_FUNC(s32, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC1(s32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2118,7 +2118,7 @@ CVTS_FUNC1(s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(s32, f32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
@@ -2169,7 +2169,7 @@ CVTS_FUNC(s32, f32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(f32, u8, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)((1 << 16)*alpha));
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)((1 << 16)*beta));
|
||||
@@ -2239,7 +2239,7 @@ CVTS_FUNC(f32, u8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(f32, s8, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2293,7 +2293,7 @@ CVTS_FUNC(f32, s8, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(f32, u16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2345,7 +2345,7 @@ CVTS_FUNC(f32, u16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(f32, s16, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2397,7 +2397,7 @@ CVTS_FUNC(f32, s16, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC(f32, s32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta + 0.5f);,
|
||||
@@ -2448,7 +2448,7 @@ CVTS_FUNC(f32, s32, 8,
|
||||
})
|
||||
#endif
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
CVTS_FUNC1(f32, 8,
|
||||
register float32x4_t vscale asm ("q0") = vdupq_n_f32((f32)alpha);
|
||||
register float32x4_t vshift asm ("q1") = vdupq_n_f32((f32)beta);,
|
||||
|
||||
+1
-1
@@ -327,7 +327,7 @@ void gaussianBlur5x5(const Size2D &size, s32 cn,
|
||||
u16* lidx1 = lane + x - 1*2;
|
||||
u16* lidx3 = lane + x + 1*2;
|
||||
u16* lidx4 = lane + x + 2*2;
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ __volatile__ (
|
||||
"vld2.16 {d0, d2}, [%[in0]]! \n\t"
|
||||
"vld2.16 {d1, d3}, [%[in0]] \n\t"
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ void pyrLKOptFlowLevel(const Size2D &size, s32 cn,
|
||||
internal::prefetch(dsrc + dstep * 2, 0);
|
||||
for(x = 0; x <= wwcn - 4; x += 4, dsrc += 4*2, dIptr += 4*2 )
|
||||
{
|
||||
#if __GNUC_MINOR__ < 0
|
||||
#if 0
|
||||
__asm__ (
|
||||
"vld2.16 {d0-d1}, [%[dsrc00]] \n\t"
|
||||
"vld2.16 {d2-d3}, [%[dsrc10]] \n\t"
|
||||
|
||||
Vendored
+4
-4
@@ -331,7 +331,7 @@ void gaussianPyramidDown(const Size2D &srcSize,
|
||||
for (; x < roiw8; x += 8)
|
||||
{
|
||||
internal::prefetch(lane + 2 * x);
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld2.16 {d0-d3}, [%[in0]] \n\t"
|
||||
"vld2.16 {d4-d7}, [%[in4]] \n\t"
|
||||
@@ -538,7 +538,7 @@ void gaussianPyramidDown(const Size2D &srcSize,
|
||||
for (; x < roiw4; x += 4)
|
||||
{
|
||||
internal::prefetch(lane + 2 * x);
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld2.32 {d0-d3}, [%[in0]] \n\t"
|
||||
"vld2.32 {d4-d7}, [%[in4]] \n\t"
|
||||
@@ -672,7 +672,7 @@ void gaussianPyramidDown(const Size2D &srcSize,
|
||||
std::vector<f32> _buf(cn*(srcSize.width + 4) + 32/sizeof(f32));
|
||||
f32* lane = internal::alignPtr(&_buf[2*cn], 32);
|
||||
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
register float32x4_t vc6d4f32 asm ("q11") = vmovq_n_f32(1.5f); // 6/4
|
||||
register float32x4_t vc1d4f32 asm ("q12") = vmovq_n_f32(0.25f); // 1/4
|
||||
|
||||
@@ -739,7 +739,7 @@ void gaussianPyramidDown(const Size2D &srcSize,
|
||||
for (; x < roiw4; x += 4)
|
||||
{
|
||||
internal::prefetch(lane + 2 * x);
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ __volatile__ (
|
||||
"vld2.32 {d0-d3}, [%[in0]] \n\t"
|
||||
"vld2.32 {d8-d11}, [%[in4]] \n\t"
|
||||
|
||||
Vendored
+3
-3
@@ -109,7 +109,7 @@ void ScharrDeriv(const Size2D &size, s32 cn,
|
||||
internal::prefetch(srow0 + x);
|
||||
internal::prefetch(srow1 + x);
|
||||
internal::prefetch(srow2 + x);
|
||||
#if __GNUC_MINOR__ < 7
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7
|
||||
__asm__ (
|
||||
"vld1.8 {d0}, [%[src0]] \n\t"
|
||||
"vld1.8 {d2}, [%[src2]] \n\t"
|
||||
@@ -161,7 +161,7 @@ void ScharrDeriv(const Size2D &size, s32 cn,
|
||||
x = 0;
|
||||
for( ; x < roiw8; x += 8 )
|
||||
{
|
||||
#if __GNUC_MINOR__ < 6
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6
|
||||
__asm__ (
|
||||
"vld1.16 {d4-d5}, [%[s2ptr]] \n\t"
|
||||
"vld1.16 {d8-d9}, [%[s4ptr]] \n\t"
|
||||
@@ -198,7 +198,7 @@ void ScharrDeriv(const Size2D &size, s32 cn,
|
||||
vr.val[1] = vmlaq_s16(s3x10, s24, vc3);
|
||||
|
||||
vst2q_s16(drow + x*2, vr);
|
||||
#endif //__GNUC_MINOR__ < 6
|
||||
#endif
|
||||
}
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
if(NOT ANDROID)
|
||||
message("cpufeatures is ANDROID project")
|
||||
endif()
|
||||
|
||||
set(CPUFEATURES_ROOT "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Android cpufeatures project sources (for example, <android-ndk>/sources/android/cpufeatures)")
|
||||
|
||||
set(CPUFEATURES_INCLUDE_DIRS ${CPUFEATURES_ROOT} CACHE INTERNAL "")
|
||||
set(CPUFEATURES_LIBRARIES cpufeatures CACHE INTERNAL "")
|
||||
|
||||
if(NOT DEFINED CPUFEATURES_SOURCES)
|
||||
set(CPUFEATURES_SOURCES ${CPUFEATURES_ROOT}/cpu-features.c ${CPUFEATURES_ROOT}/cpu-features.h)
|
||||
endif()
|
||||
|
||||
include_directories(${CPUFEATURES_INCLUDE_DIRS})
|
||||
add_library(cpufeatures STATIC ${CPUFEATURES_SOURCES})
|
||||
|
||||
set_target_properties(cpufeatures
|
||||
PROPERTIES OUTPUT_NAME cpufeatures
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
COMPILE_PDB_NAME cpufeatures
|
||||
COMPILE_PDB_NAME_DEBUG "cpufeatures${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(cpufeatures PROPERTIES FOLDER "3rdparty")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(cpufeatures EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
||||
endif()
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
The Android NDK provides a small library named cpufeatures that your app can use at runtime to detect the target device's CPU family and the optional features it supports.
|
||||
It is designed to work as-is on all official Android platform versions.
|
||||
|
||||
https://developer.android.com/ndk/guides/cpu-features.html
|
||||
+430
-69
@@ -27,6 +27,10 @@
|
||||
*/
|
||||
|
||||
/* ChangeLog for this library:
|
||||
*
|
||||
* NDK r10e?: Add MIPS MSA feature.
|
||||
*
|
||||
* NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS).
|
||||
*
|
||||
* NDK r8d: Add android_setCpu().
|
||||
*
|
||||
@@ -56,16 +60,17 @@
|
||||
*
|
||||
* NDK r4: Initial release
|
||||
*/
|
||||
#include <sys/system_properties.h>
|
||||
#ifdef __arm__
|
||||
#include <machine/cpu-features.h>
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
|
||||
#include "cpu-features.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static pthread_once_t g_once;
|
||||
static int g_inited;
|
||||
@@ -73,16 +78,12 @@ static AndroidCpuFamily g_cpuFamily;
|
||||
static uint64_t g_cpuFeatures;
|
||||
static int g_cpuCount;
|
||||
|
||||
static const int android_cpufeatures_debug = 0;
|
||||
|
||||
#ifdef __arm__
|
||||
# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM
|
||||
#elif defined __i386__
|
||||
# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86
|
||||
#else
|
||||
# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN
|
||||
static uint32_t g_cpuIdArm;
|
||||
#endif
|
||||
|
||||
static const int android_cpufeatures_debug = 0;
|
||||
|
||||
#define D(...) \
|
||||
do { \
|
||||
if (android_cpufeatures_debug) { \
|
||||
@@ -109,6 +110,25 @@ static __inline__ void x86_cpuid(int func, int values[4])
|
||||
values[2] = c;
|
||||
values[3] = d;
|
||||
}
|
||||
#elif defined(__x86_64__)
|
||||
static __inline__ void x86_cpuid(int func, int values[4])
|
||||
{
|
||||
int64_t a, b, c, d;
|
||||
/* We need to preserve ebx since we're compiling PIC code */
|
||||
/* this means we can't use "=b" for the second output register */
|
||||
__asm__ __volatile__ ( \
|
||||
"push %%rbx\n"
|
||||
"cpuid\n" \
|
||||
"mov %%rbx, %1\n"
|
||||
"pop %%rbx\n"
|
||||
: "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
|
||||
: "a" (func) \
|
||||
);
|
||||
values[0] = a;
|
||||
values[1] = b;
|
||||
values[2] = c;
|
||||
values[3] = d;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get the size of a file by reading it until the end. This is needed
|
||||
@@ -118,7 +138,8 @@ static __inline__ void x86_cpuid(int func, int values[4])
|
||||
static int
|
||||
get_file_size(const char* pathname)
|
||||
{
|
||||
int fd, ret, result = 0;
|
||||
|
||||
int fd, result = 0;
|
||||
char buffer[256];
|
||||
|
||||
fd = open(pathname, O_RDONLY);
|
||||
@@ -178,6 +199,7 @@ read_file(const char* pathname, char* buffer, size_t buffsize)
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
/* Extract the content of a the first occurence of a given field in
|
||||
* the content of /proc/cpuinfo and return it as a heap-allocated
|
||||
* string that must be freed by the caller.
|
||||
@@ -190,12 +212,11 @@ extract_cpuinfo_field(const char* buffer, int buflen, const char* field)
|
||||
int fieldlen = strlen(field);
|
||||
const char* bufend = buffer + buflen;
|
||||
char* result = NULL;
|
||||
int len, ignore;
|
||||
int len;
|
||||
const char *p, *q;
|
||||
|
||||
/* Look for first field occurence, and ensures it starts the line. */
|
||||
p = buffer;
|
||||
bufend = buffer + buflen;
|
||||
for (;;) {
|
||||
p = memmem(p, bufend-p, field, fieldlen);
|
||||
if (p == NULL)
|
||||
@@ -232,10 +253,6 @@ EXIT:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Like strlen(), but for constant string literals */
|
||||
#define STRLEN_CONST(x) ((sizeof(x)-1)
|
||||
|
||||
|
||||
/* Checks that a space-separated list of items contains one given 'item'.
|
||||
* Returns 1 if found, 0 otherwise.
|
||||
*/
|
||||
@@ -268,8 +285,9 @@ has_list_item(const char* list, const char* item)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* __arm__ */
|
||||
|
||||
/* Parse an decimal integer starting from 'input', but not going further
|
||||
/* Parse a number starting from 'input', but not going further
|
||||
* than 'limit'. Return the value into '*result'.
|
||||
*
|
||||
* NOTE: Does not skip over leading spaces, or deal with sign characters.
|
||||
@@ -280,15 +298,23 @@ has_list_item(const char* list, const char* item)
|
||||
* be <= 'limit').
|
||||
*/
|
||||
static const char*
|
||||
parse_decimal(const char* input, const char* limit, int* result)
|
||||
parse_number(const char* input, const char* limit, int base, int* result)
|
||||
{
|
||||
const char* p = input;
|
||||
int val = 0;
|
||||
while (p < limit) {
|
||||
int d = (*p - '0');
|
||||
if ((unsigned)d >= 10U)
|
||||
break;
|
||||
val = val*10 + d;
|
||||
if ((unsigned)d >= 10U) {
|
||||
d = (*p - 'a');
|
||||
if ((unsigned)d >= 6U)
|
||||
d = (*p - 'A');
|
||||
if ((unsigned)d >= 6U)
|
||||
break;
|
||||
d += 10;
|
||||
}
|
||||
if (d >= base)
|
||||
break;
|
||||
val = val*base + d;
|
||||
p++;
|
||||
}
|
||||
if (p == input)
|
||||
@@ -298,6 +324,20 @@ parse_decimal(const char* input, const char* limit, int* result)
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char*
|
||||
parse_decimal(const char* input, const char* limit, int* result)
|
||||
{
|
||||
return parse_number(input, limit, 10, result);
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
static const char*
|
||||
parse_hexadecimal(const char* input, const char* limit, int* result)
|
||||
{
|
||||
return parse_number(input, limit, 16, result);
|
||||
}
|
||||
#endif /* __arm__ */
|
||||
|
||||
/* This small data type is used to represent a CPU list / mask, as read
|
||||
* from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt
|
||||
*
|
||||
@@ -408,6 +448,18 @@ cpulist_read_from(CpuList* list, const char* filename)
|
||||
|
||||
cpulist_parse(list, file, filelen);
|
||||
}
|
||||
#if defined(__aarch64__)
|
||||
// see <uapi/asm/hwcap.h> kernel header
|
||||
#define HWCAP_FP (1 << 0)
|
||||
#define HWCAP_ASIMD (1 << 1)
|
||||
#define HWCAP_AES (1 << 3)
|
||||
#define HWCAP_PMULL (1 << 4)
|
||||
#define HWCAP_SHA1 (1 << 5)
|
||||
#define HWCAP_SHA2 (1 << 6)
|
||||
#define HWCAP_CRC32 (1 << 7)
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
// See <asm/hwcap.h> kernel header.
|
||||
#define HWCAP_VFP (1 << 6)
|
||||
@@ -419,27 +471,104 @@ cpulist_read_from(CpuList* list, const char* filename)
|
||||
#define HWCAP_IDIVA (1 << 17)
|
||||
#define HWCAP_IDIVT (1 << 18)
|
||||
|
||||
// see <uapi/asm/hwcap.h> kernel header
|
||||
#define HWCAP2_AES (1 << 0)
|
||||
#define HWCAP2_PMULL (1 << 1)
|
||||
#define HWCAP2_SHA1 (1 << 2)
|
||||
#define HWCAP2_SHA2 (1 << 3)
|
||||
#define HWCAP2_CRC32 (1 << 4)
|
||||
|
||||
// This is the list of 32-bit ARMv7 optional features that are _always_
|
||||
// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference
|
||||
// Manual.
|
||||
#define HWCAP_SET_FOR_ARMV8 \
|
||||
( HWCAP_VFP | \
|
||||
HWCAP_NEON | \
|
||||
HWCAP_VFPv3 | \
|
||||
HWCAP_VFPv4 | \
|
||||
HWCAP_IDIVA | \
|
||||
HWCAP_IDIVT )
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
// see <uapi/asm/hwcap.h> kernel header
|
||||
#define HWCAP_MIPS_R6 (1 << 0)
|
||||
#define HWCAP_MIPS_MSA (1 << 1)
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
|
||||
|
||||
#define AT_HWCAP 16
|
||||
#define AT_HWCAP2 26
|
||||
|
||||
// Probe the system's C library for a 'getauxval' function and call it if
|
||||
// it exits, or return 0 for failure. This function is available since API
|
||||
// level 20.
|
||||
//
|
||||
// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the
|
||||
// edge case where some NDK developers use headers for a platform that is
|
||||
// newer than the one really targetted by their application.
|
||||
// This is typically done to use newer native APIs only when running on more
|
||||
// recent Android versions, and requires careful symbol management.
|
||||
//
|
||||
// Note that getauxval() can't really be re-implemented here, because
|
||||
// its implementation does not parse /proc/self/auxv. Instead it depends
|
||||
// on values that are passed by the kernel at process-init time to the
|
||||
// C runtime initialization layer.
|
||||
#if 1
|
||||
// OpenCV calls CPU features check during library initialization stage
|
||||
// (under other dlopen() call).
|
||||
// Unfortunatelly, calling dlopen() recursively is not supported on some old
|
||||
// Android versions. Android fix is here:
|
||||
// - https://android-review.googlesource.com/#/c/32951/
|
||||
// - GitHub mirror: https://github.com/android/platform_bionic/commit/e19d702b8e330cef87e0983733c427b5f7842144
|
||||
__attribute__((weak)) unsigned long getauxval(unsigned long); // Lets linker to handle this symbol
|
||||
static uint32_t
|
||||
get_elf_hwcap_from_getauxval(int hwcap_type) {
|
||||
uint32_t ret = 0;
|
||||
if(getauxval != 0) {
|
||||
ret = (uint32_t)getauxval(hwcap_type);
|
||||
} else {
|
||||
D("getauxval() is not available\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static uint32_t
|
||||
get_elf_hwcap_from_getauxval(int hwcap_type) {
|
||||
typedef unsigned long getauxval_func_t(unsigned long);
|
||||
|
||||
dlerror();
|
||||
void* libc_handle = dlopen("libc.so", RTLD_NOW);
|
||||
if (!libc_handle) {
|
||||
D("Could not dlopen() C library: %s\n", dlerror());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ret = 0;
|
||||
getauxval_func_t* func = (getauxval_func_t*)
|
||||
dlsym(libc_handle, "getauxval");
|
||||
if (!func) {
|
||||
D("Could not find getauxval() in C library\n");
|
||||
} else {
|
||||
// Note: getauxval() returns 0 on failure. Doesn't touch errno.
|
||||
ret = (uint32_t)(*func)(hwcap_type);
|
||||
}
|
||||
dlclose(libc_handle);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
/* Compute the ELF HWCAP flags.
|
||||
*/
|
||||
// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the
|
||||
// current CPU. Note that this file is not accessible from regular
|
||||
// application processes on some Android platform releases.
|
||||
// On success, return new ELF hwcaps, or 0 on failure.
|
||||
static uint32_t
|
||||
get_elf_hwcap(const char* cpuinfo, int cpuinfo_len)
|
||||
{
|
||||
/* IMPORTANT:
|
||||
* Accessing /proc/self/auxv doesn't work anymore on all
|
||||
* platform versions. More specifically, when running inside
|
||||
* a regular application process, most of /proc/self/ will be
|
||||
* non-readable, including /proc/self/auxv. This doesn't
|
||||
* happen however if the application is debuggable, or when
|
||||
* running under the "shell" UID, which is why this was not
|
||||
* detected appropriately.
|
||||
*/
|
||||
#if 0
|
||||
uint32_t result = 0;
|
||||
get_elf_hwcap_from_proc_self_auxv(void) {
|
||||
const char filepath[] = "/proc/self/auxv";
|
||||
int fd = open(filepath, O_RDONLY);
|
||||
int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
D("Could not open %s: %s\n", filepath, strerror(errno));
|
||||
return 0;
|
||||
@@ -447,11 +576,10 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len)
|
||||
|
||||
struct { uint32_t tag; uint32_t value; } entry;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (;;) {
|
||||
int ret = read(fd, (char*)&entry, sizeof entry);
|
||||
int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry));
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
D("Error while reading %s: %s\n", filepath, strerror(errno));
|
||||
break;
|
||||
}
|
||||
@@ -465,12 +593,33 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len)
|
||||
}
|
||||
close(fd);
|
||||
return result;
|
||||
#else
|
||||
// Recreate ELF hwcaps by parsing /proc/cpuinfo Features tag.
|
||||
}
|
||||
|
||||
/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo.
|
||||
* This works by parsing the 'Features' line, which lists which optional
|
||||
* features the device's CPU supports, on top of its reference
|
||||
* architecture.
|
||||
*/
|
||||
static uint32_t
|
||||
get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len) {
|
||||
uint32_t hwcaps = 0;
|
||||
long architecture = 0;
|
||||
char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
|
||||
if (cpuArch) {
|
||||
architecture = strtol(cpuArch, NULL, 10);
|
||||
free(cpuArch);
|
||||
|
||||
if (architecture >= 8L) {
|
||||
// This is a 32-bit ARM binary running on a 64-bit ARM64 kernel.
|
||||
// The 'Features' line only lists the optional features that the
|
||||
// device's CPU supports, compared to its reference architecture
|
||||
// which are of no use for this process.
|
||||
D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture);
|
||||
return HWCAP_SET_FOR_ARMV8;
|
||||
}
|
||||
}
|
||||
|
||||
char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
|
||||
|
||||
if (cpuFeatures != NULL) {
|
||||
D("Found cpuFeatures = '%s'\n", cpuFeatures);
|
||||
|
||||
@@ -496,7 +645,6 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len)
|
||||
free(cpuFeatures);
|
||||
}
|
||||
return hwcaps;
|
||||
#endif
|
||||
}
|
||||
#endif /* __arm__ */
|
||||
|
||||
@@ -526,12 +674,19 @@ get_cpu_count(void)
|
||||
static void
|
||||
android_cpuInitFamily(void)
|
||||
{
|
||||
#if defined(__ARM_ARCH__)
|
||||
#if defined(__arm__)
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
|
||||
#elif defined(__i386__)
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_X86;
|
||||
#elif defined(_MIPS_ARCH)
|
||||
#elif defined(__mips64)
|
||||
/* Needs to be before __mips__ since the compiler defines both */
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64;
|
||||
#elif defined(__mips__)
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_MIPS;
|
||||
#elif defined(__aarch64__)
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_ARM64;
|
||||
#elif defined(__x86_64__)
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_X86_64;
|
||||
#else
|
||||
g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN;
|
||||
#endif
|
||||
@@ -576,11 +731,8 @@ android_cpuInit(void)
|
||||
|
||||
D("found cpuCount = %d\n", g_cpuCount);
|
||||
|
||||
#ifdef __ARM_ARCH__
|
||||
#ifdef __arm__
|
||||
{
|
||||
char* features = NULL;
|
||||
char* architecture = NULL;
|
||||
|
||||
/* Extract architecture from the "CPU Architecture" field.
|
||||
* The list is well-known, unlike the the output of
|
||||
* the 'Processor' field which can vary greatly.
|
||||
@@ -601,10 +753,7 @@ android_cpuInit(void)
|
||||
/* read the initial decimal number, ignore the rest */
|
||||
archNumber = strtol(cpuArch, &end, 10);
|
||||
|
||||
/* Here we assume that ARMv8 will be upwards compatible with v7
|
||||
* in the future. Unfortunately, there is no 'Features' field to
|
||||
* indicate that Thumb-2 is supported.
|
||||
*/
|
||||
/* Note that ARMv8 is upwards compatible with ARMv7. */
|
||||
if (end > cpuArch && archNumber >= 7) {
|
||||
hasARMv7 = 1;
|
||||
}
|
||||
@@ -645,7 +794,19 @@ android_cpuInit(void)
|
||||
}
|
||||
|
||||
/* Extract the list of CPU features from ELF hwcaps */
|
||||
uint32_t hwcaps = get_elf_hwcap(cpuinfo, cpuinfo_len);
|
||||
uint32_t hwcaps = 0;
|
||||
hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
|
||||
if (!hwcaps) {
|
||||
D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
|
||||
hwcaps = get_elf_hwcap_from_proc_self_auxv();
|
||||
}
|
||||
if (!hwcaps) {
|
||||
// Parsing /proc/self/auxv will fail from regular application
|
||||
// processes on some Android platform versions, when this happens
|
||||
// parse proc/cpuinfo instead.
|
||||
D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n");
|
||||
hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len);
|
||||
}
|
||||
|
||||
if (hwcaps != 0) {
|
||||
int has_vfp = (hwcaps & HWCAP_VFP);
|
||||
@@ -697,22 +858,163 @@ android_cpuInit(void)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 |
|
||||
ANDROID_CPU_ARM_FEATURE_ARMv7;
|
||||
|
||||
// Note that some buggy kernels do not report these even when
|
||||
// the CPU actually support the division instructions. However,
|
||||
// assume that if 'vfpv4' is detected, then the CPU supports
|
||||
// sdiv/udiv properly.
|
||||
if (has_idiva || has_vfpv4)
|
||||
if (has_idiva)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
|
||||
if (has_idivt || has_vfpv4)
|
||||
if (has_idivt)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2;
|
||||
|
||||
if (has_iwmmxt)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt;
|
||||
}
|
||||
}
|
||||
#endif /* __ARM_ARCH__ */
|
||||
|
||||
#ifdef __i386__
|
||||
/* Extract the list of CPU features from ELF hwcaps2 */
|
||||
uint32_t hwcaps2 = 0;
|
||||
hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2);
|
||||
if (hwcaps2 != 0) {
|
||||
int has_aes = (hwcaps2 & HWCAP2_AES);
|
||||
int has_pmull = (hwcaps2 & HWCAP2_PMULL);
|
||||
int has_sha1 = (hwcaps2 & HWCAP2_SHA1);
|
||||
int has_sha2 = (hwcaps2 & HWCAP2_SHA2);
|
||||
int has_crc32 = (hwcaps2 & HWCAP2_CRC32);
|
||||
|
||||
if (has_aes)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES;
|
||||
if (has_pmull)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL;
|
||||
if (has_sha1)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1;
|
||||
if (has_sha2)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2;
|
||||
if (has_crc32)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32;
|
||||
}
|
||||
/* Extract the cpuid value from various fields */
|
||||
// The CPUID value is broken up in several entries in /proc/cpuinfo.
|
||||
// This table is used to rebuild it from the entries.
|
||||
static const struct CpuIdEntry {
|
||||
const char* field;
|
||||
char format;
|
||||
char bit_lshift;
|
||||
char bit_length;
|
||||
} cpu_id_entries[] = {
|
||||
{ "CPU implementer", 'x', 24, 8 },
|
||||
{ "CPU variant", 'x', 20, 4 },
|
||||
{ "CPU part", 'x', 4, 12 },
|
||||
{ "CPU revision", 'd', 0, 4 },
|
||||
};
|
||||
size_t i;
|
||||
D("Parsing /proc/cpuinfo to recover CPUID\n");
|
||||
for (i = 0;
|
||||
i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]);
|
||||
++i) {
|
||||
const struct CpuIdEntry* entry = &cpu_id_entries[i];
|
||||
char* value = extract_cpuinfo_field(cpuinfo,
|
||||
cpuinfo_len,
|
||||
entry->field);
|
||||
if (value == NULL)
|
||||
continue;
|
||||
|
||||
D("field=%s value='%s'\n", entry->field, value);
|
||||
char* value_end = value + strlen(value);
|
||||
int val = 0;
|
||||
const char* start = value;
|
||||
const char* p;
|
||||
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) {
|
||||
start += 2;
|
||||
p = parse_hexadecimal(start, value_end, &val);
|
||||
} else if (entry->format == 'x')
|
||||
p = parse_hexadecimal(value, value_end, &val);
|
||||
else
|
||||
p = parse_decimal(value, value_end, &val);
|
||||
|
||||
if (p > (const char*)start) {
|
||||
val &= ((1 << entry->bit_length)-1);
|
||||
val <<= entry->bit_lshift;
|
||||
g_cpuIdArm |= (uint32_t) val;
|
||||
}
|
||||
|
||||
free(value);
|
||||
}
|
||||
|
||||
// Handle kernel configuration bugs that prevent the correct
|
||||
// reporting of CPU features.
|
||||
static const struct CpuFix {
|
||||
uint32_t cpuid;
|
||||
uint64_t or_flags;
|
||||
} cpu_fixes[] = {
|
||||
/* The Nexus 4 (Qualcomm Krait) kernel configuration
|
||||
* forgets to report IDIV support. */
|
||||
{ 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM |
|
||||
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
|
||||
{ 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM |
|
||||
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
|
||||
};
|
||||
size_t n;
|
||||
for (n = 0; n < sizeof(cpu_fixes)/sizeof(cpu_fixes[0]); ++n) {
|
||||
const struct CpuFix* entry = &cpu_fixes[n];
|
||||
|
||||
if (g_cpuIdArm == entry->cpuid)
|
||||
g_cpuFeatures |= entry->or_flags;
|
||||
}
|
||||
|
||||
// Special case: The emulator-specific Android 4.2 kernel fails
|
||||
// to report support for the 32-bit ARM IDIV instruction.
|
||||
// Technically, this is a feature of the virtual CPU implemented
|
||||
// by the emulator. Note that it could also support Thumb IDIV
|
||||
// in the future, and this will have to be slightly updated.
|
||||
char* hardware = extract_cpuinfo_field(cpuinfo,
|
||||
cpuinfo_len,
|
||||
"Hardware");
|
||||
if (hardware) {
|
||||
if (!strcmp(hardware, "Goldfish") &&
|
||||
g_cpuIdArm == 0x4100c080 &&
|
||||
(g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
|
||||
}
|
||||
free(hardware);
|
||||
}
|
||||
}
|
||||
#endif /* __arm__ */
|
||||
#ifdef __aarch64__
|
||||
{
|
||||
/* Extract the list of CPU features from ELF hwcaps */
|
||||
uint32_t hwcaps = 0;
|
||||
hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
|
||||
if (hwcaps != 0) {
|
||||
int has_fp = (hwcaps & HWCAP_FP);
|
||||
int has_asimd = (hwcaps & HWCAP_ASIMD);
|
||||
int has_aes = (hwcaps & HWCAP_AES);
|
||||
int has_pmull = (hwcaps & HWCAP_PMULL);
|
||||
int has_sha1 = (hwcaps & HWCAP_SHA1);
|
||||
int has_sha2 = (hwcaps & HWCAP_SHA2);
|
||||
int has_crc32 = (hwcaps & HWCAP_CRC32);
|
||||
|
||||
if(has_fp == 0) {
|
||||
D("ERROR: Floating-point unit missing, but is required by Android on AArch64 CPUs\n");
|
||||
}
|
||||
if(has_asimd == 0) {
|
||||
D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n");
|
||||
}
|
||||
|
||||
if (has_fp)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP;
|
||||
if (has_asimd)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD;
|
||||
if (has_aes)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES;
|
||||
if (has_pmull)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL;
|
||||
if (has_sha1)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1;
|
||||
if (has_sha2)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2;
|
||||
if (has_crc32)
|
||||
g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32;
|
||||
}
|
||||
}
|
||||
#endif /* __aarch64__ */
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
int regs[4];
|
||||
|
||||
/* According to http://en.wikipedia.org/wiki/CPUID */
|
||||
@@ -732,10 +1034,50 @@ android_cpuInit(void)
|
||||
if ((regs[2] & (1 << 23)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
|
||||
}
|
||||
if ((regs[2] & (1 << 19)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1;
|
||||
}
|
||||
if ((regs[2] & (1 << 20)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2;
|
||||
}
|
||||
if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
|
||||
}
|
||||
if ((regs[2] & (1 << 25)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI;
|
||||
}
|
||||
if ((regs[2] & (1 << 28)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX;
|
||||
}
|
||||
if ((regs[2] & (1 << 30)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND;
|
||||
}
|
||||
|
||||
x86_cpuid(7, regs);
|
||||
if ((regs[1] & (1 << 5)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2;
|
||||
}
|
||||
if ((regs[1] & (1 << 29)) != 0) {
|
||||
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
#if defined( __mips__)
|
||||
{ /* MIPS and MIPS64 */
|
||||
/* Extract the list of CPU features from ELF hwcaps */
|
||||
uint32_t hwcaps = 0;
|
||||
hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
|
||||
if (hwcaps != 0) {
|
||||
int has_r6 = (hwcaps & HWCAP_MIPS_R6);
|
||||
int has_msa = (hwcaps & HWCAP_MIPS_MSA);
|
||||
if (has_r6)
|
||||
g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6;
|
||||
if (has_msa)
|
||||
g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA;
|
||||
}
|
||||
}
|
||||
#endif /* __mips__ */
|
||||
|
||||
free(cpuinfo);
|
||||
}
|
||||
@@ -785,6 +1127,25 @@ android_setCpu(int cpu_count, uint64_t cpu_features)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
uint32_t
|
||||
android_getCpuIdArm(void)
|
||||
{
|
||||
pthread_once(&g_once, android_cpuInit);
|
||||
return g_cpuIdArm;
|
||||
}
|
||||
|
||||
int
|
||||
android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id)
|
||||
{
|
||||
if (!android_setCpu(cpu_count, cpu_features))
|
||||
return 0;
|
||||
|
||||
g_cpuIdArm = cpu_id;
|
||||
return 1;
|
||||
}
|
||||
#endif /* __arm__ */
|
||||
|
||||
/*
|
||||
* Technical note: Making sense of ARM's FPU architecture versions.
|
||||
*
|
||||
+134
-6
@@ -33,21 +33,40 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* A list of valid values returned by android_getCpuFamily().
|
||||
* They describe the CPU Architecture of the current process.
|
||||
*/
|
||||
typedef enum {
|
||||
ANDROID_CPU_FAMILY_UNKNOWN = 0,
|
||||
ANDROID_CPU_FAMILY_ARM,
|
||||
ANDROID_CPU_FAMILY_X86,
|
||||
ANDROID_CPU_FAMILY_MIPS,
|
||||
ANDROID_CPU_FAMILY_ARM64,
|
||||
ANDROID_CPU_FAMILY_X86_64,
|
||||
ANDROID_CPU_FAMILY_MIPS64,
|
||||
|
||||
ANDROID_CPU_FAMILY_MAX /* do not remove */
|
||||
|
||||
} AndroidCpuFamily;
|
||||
|
||||
/* Return family of the device's CPU */
|
||||
extern AndroidCpuFamily android_getCpuFamily(void);
|
||||
/* Return the CPU family of the current process.
|
||||
*
|
||||
* Note that this matches the bitness of the current process. I.e. when
|
||||
* running a 32-bit binary on a 64-bit capable CPU, this will return the
|
||||
* 32-bit CPU family value.
|
||||
*/
|
||||
extern AndroidCpuFamily android_getCpuFamily(void);
|
||||
|
||||
/* The list of feature flags for ARM CPUs that can be recognized by the
|
||||
* library. Value details are:
|
||||
/* Return a bitmap describing a set of optional CPU features that are
|
||||
* supported by the current device's CPU. The exact bit-flags returned
|
||||
* depend on the value returned by android_getCpuFamily(). See the
|
||||
* documentation for the ANDROID_CPU_*_FEATURE_* flags below for details.
|
||||
*/
|
||||
extern uint64_t android_getCpuFeatures(void);
|
||||
|
||||
/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be
|
||||
* recognized by the library (see note below for 64-bit ARM). Value details
|
||||
* are:
|
||||
*
|
||||
* VFPv2:
|
||||
* CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs
|
||||
@@ -103,6 +122,27 @@ extern AndroidCpuFamily android_getCpuFamily(void);
|
||||
* ARM CPU. This is only available on a few XScale-based CPU designs
|
||||
* sold by Marvell. Pretty rare in practice.
|
||||
*
|
||||
* AES:
|
||||
* CPU supports AES instructions. These instructions are only
|
||||
* available for 32-bit applications running on ARMv8 CPU.
|
||||
*
|
||||
* CRC32:
|
||||
* CPU supports CRC32 instructions. These instructions are only
|
||||
* available for 32-bit applications running on ARMv8 CPU.
|
||||
*
|
||||
* SHA2:
|
||||
* CPU supports SHA2 instructions. These instructions are only
|
||||
* available for 32-bit applications running on ARMv8 CPU.
|
||||
*
|
||||
* SHA1:
|
||||
* CPU supports SHA1 instructions. These instructions are only
|
||||
* available for 32-bit applications running on ARMv8 CPU.
|
||||
*
|
||||
* PMULL:
|
||||
* CPU supports 64-bit PMULL and PMULL2 instructions. These
|
||||
* instructions are only available for 32-bit applications
|
||||
* running on ARMv8 CPU.
|
||||
*
|
||||
* If you want to tell the compiler to generate code that targets one of
|
||||
* the feature set above, you should probably use one of the following
|
||||
* flags (for more details, see technical note at the end of this file):
|
||||
@@ -150,6 +190,13 @@ extern AndroidCpuFamily android_getCpuFamily(void);
|
||||
*
|
||||
* -mcpu=iwmmxt
|
||||
* Allows the use of iWMMXt instrinsics with GCC.
|
||||
*
|
||||
* IMPORTANT NOTE: These flags should only be tested when
|
||||
* android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a
|
||||
* 32-bit process.
|
||||
*
|
||||
* When running a 64-bit ARM process on an ARMv8 CPU,
|
||||
* android_getCpuFeatures() will return a different set of bitflags
|
||||
*/
|
||||
enum {
|
||||
ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
|
||||
@@ -164,18 +211,84 @@ enum {
|
||||
ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9),
|
||||
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10),
|
||||
ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11),
|
||||
ANDROID_CPU_ARM_FEATURE_AES = (1 << 12),
|
||||
ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13),
|
||||
ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14),
|
||||
ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15),
|
||||
ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16),
|
||||
};
|
||||
|
||||
/* The bit flags corresponding to the output of android_getCpuFeatures()
|
||||
* when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details
|
||||
* are:
|
||||
*
|
||||
* FP:
|
||||
* CPU has Floating-point unit.
|
||||
*
|
||||
* ASIMD:
|
||||
* CPU has Advanced SIMD unit.
|
||||
*
|
||||
* AES:
|
||||
* CPU supports AES instructions.
|
||||
*
|
||||
* CRC32:
|
||||
* CPU supports CRC32 instructions.
|
||||
*
|
||||
* SHA2:
|
||||
* CPU supports SHA2 instructions.
|
||||
*
|
||||
* SHA1:
|
||||
* CPU supports SHA1 instructions.
|
||||
*
|
||||
* PMULL:
|
||||
* CPU supports 64-bit PMULL and PMULL2 instructions.
|
||||
*/
|
||||
enum {
|
||||
ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0),
|
||||
ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1),
|
||||
ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2),
|
||||
ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3),
|
||||
ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4),
|
||||
ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5),
|
||||
ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6),
|
||||
};
|
||||
|
||||
/* The bit flags corresponding to the output of android_getCpuFeatures()
|
||||
* when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or
|
||||
* ANDROID_CPU_FAMILY_X86_64.
|
||||
*/
|
||||
enum {
|
||||
ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0),
|
||||
ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1),
|
||||
ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2),
|
||||
ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3),
|
||||
ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4),
|
||||
ANDROID_CPU_X86_FEATURE_AES_NI = (1 << 5),
|
||||
ANDROID_CPU_X86_FEATURE_AVX = (1 << 6),
|
||||
ANDROID_CPU_X86_FEATURE_RDRAND = (1 << 7),
|
||||
ANDROID_CPU_X86_FEATURE_AVX2 = (1 << 8),
|
||||
ANDROID_CPU_X86_FEATURE_SHA_NI = (1 << 9),
|
||||
};
|
||||
|
||||
/* The bit flags corresponding to the output of android_getCpuFeatures()
|
||||
* when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS
|
||||
* or ANDROID_CPU_FAMILY_MIPS64. Values are:
|
||||
*
|
||||
* R6:
|
||||
* CPU executes MIPS Release 6 instructions natively, and
|
||||
* supports obsoleted R1..R5 instructions only via kernel traps.
|
||||
*
|
||||
* MSA:
|
||||
* CPU supports Mips SIMD Architecture instructions.
|
||||
*/
|
||||
enum {
|
||||
ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0),
|
||||
ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1),
|
||||
};
|
||||
|
||||
extern uint64_t android_getCpuFeatures(void);
|
||||
|
||||
/* Return the number of CPU cores detected on this device. */
|
||||
extern int android_getCpuCount(void);
|
||||
extern int android_getCpuCount(void);
|
||||
|
||||
/* The following is used to force the CPU count and features
|
||||
* mask in sandboxed processes. Under 4.1 and higher, these processes
|
||||
@@ -190,6 +303,21 @@ extern int android_getCpuCount(void);
|
||||
extern int android_setCpu(int cpu_count,
|
||||
uint64_t cpu_features);
|
||||
|
||||
#ifdef __arm__
|
||||
/* Retrieve the ARM 32-bit CPUID value from the kernel.
|
||||
* Note that this cannot work on sandboxed processes under 4.1 and
|
||||
* higher, unless you called android_setCpuArm() before.
|
||||
*/
|
||||
extern uint32_t android_getCpuIdArm(void);
|
||||
|
||||
/* An ARM-specific variant of android_setCpu() that also allows you
|
||||
* to set the ARM CPUID field.
|
||||
*/
|
||||
extern int android_setCpuArm(int cpu_count,
|
||||
uint64_t cpu_features,
|
||||
uint32_t cpu_id);
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* CPU_FEATURES_H */
|
||||
Vendored
+33
-21
@@ -1,25 +1,37 @@
|
||||
# Binary branch name: ffmpeg/master_20161202
|
||||
# Binaries were created for OpenCV: 594c136d1fcbb5816c57516e50f9cbeffbd90835
|
||||
set(FFMPEG_BINARIES_COMMIT "2a19d0006415955c79431116e4634f04d5eb5a74")
|
||||
set(FFMPEG_FILE_HASH_BIN32 "f081abd9d6ca7e425d340ce586f9c090")
|
||||
set(FFMPEG_FILE_HASH_BIN64 "a423363a6eb76d362ca6c406c96c8db6")
|
||||
set(FFMPEG_FILE_HASH_CMAKE "5346ae1854fc7aa569a722e85af480ec")
|
||||
# Binary branch name: ffmpeg/master_20170418
|
||||
# Binaries were created for OpenCV: b993b9b7c7f6f5f37d10acacb2962812228410ba
|
||||
set(FFMPEG_BINARIES_COMMIT "86c4a841055f2612774e85b4292bb20e5fe8a783")
|
||||
set(FFMPEG_FILE_HASH_BIN32 "3dea5f7f009b44601fe95728328e0f9e")
|
||||
set(FFMPEG_FILE_HASH_BIN64 "9debe701975ef074bd6661981f3f0716")
|
||||
set(FFMPEG_FILE_HASH_CMAKE "208c00f03d2f6f39fa6262649e0bfc8d")
|
||||
|
||||
set(FFMPEG_DOWNLOAD_URL ${OPENCV_FFMPEG_URL};$ENV{OPENCV_FFMPEG_URL};https://raw.githubusercontent.com/opencv/opencv_3rdparty/${FFMPEG_BINARIES_COMMIT}/ffmpeg/)
|
||||
function(download_win_ffmpeg script_var)
|
||||
set(${script_var} "" PARENT_SCOPE)
|
||||
|
||||
ocv_download(PACKAGE opencv_ffmpeg.dll
|
||||
HASH ${FFMPEG_FILE_HASH_BIN32}
|
||||
URL ${FFMPEG_DOWNLOAD_URL}
|
||||
DESTINATION_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(ids BIN32 BIN64 CMAKE)
|
||||
set(name_BIN32 "opencv_ffmpeg.dll")
|
||||
set(name_BIN64 "opencv_ffmpeg_64.dll")
|
||||
set(name_CMAKE "ffmpeg_version.cmake")
|
||||
|
||||
ocv_download(PACKAGE opencv_ffmpeg_64.dll
|
||||
HASH ${FFMPEG_FILE_HASH_BIN64}
|
||||
URL ${FFMPEG_DOWNLOAD_URL}
|
||||
DESTINATION_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(FFMPEG_DOWNLOAD_DIR "${OpenCV_BINARY_DIR}/3rdparty/ffmpeg")
|
||||
|
||||
ocv_download(PACKAGE ffmpeg_version.cmake
|
||||
HASH ${FFMPEG_FILE_HASH_CMAKE}
|
||||
URL ${FFMPEG_DOWNLOAD_URL}
|
||||
DESTINATION_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ffmpeg_version.cmake)
|
||||
set(status TRUE)
|
||||
foreach(id ${ids})
|
||||
ocv_download(FILENAME ${name_${id}}
|
||||
HASH ${FFMPEG_FILE_HASH_${id}}
|
||||
URL
|
||||
"$ENV{OPENCV_FFMPEG_URL}"
|
||||
"${OPENCV_FFMPEG_URL}"
|
||||
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/${FFMPEG_BINARIES_COMMIT}/ffmpeg/"
|
||||
DESTINATION_DIR ${FFMPEG_DOWNLOAD_DIR}
|
||||
ID FFMPEG
|
||||
RELATIVE_URL
|
||||
STATUS res)
|
||||
if(NOT res)
|
||||
set(status FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
if(status)
|
||||
set(${script_var} "${FFMPEG_DOWNLOAD_DIR}/ffmpeg_version.cmake" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
downloads/
|
||||
unpack/
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# CMake file for IPP IW. See root CMakeLists.txt
|
||||
#
|
||||
# ----------------------------------------------------------------------------
|
||||
project(${IPP_IW_LIBRARY})
|
||||
|
||||
ocv_include_directories(${IPP_INCLUDE_DIRS} ${IPP_IW_PATH}/include)
|
||||
add_definitions(-DIW_BUILD)
|
||||
if(HAVE_IPP_ICV_ONLY)
|
||||
add_definitions(-DICV_BASE)
|
||||
endif()
|
||||
|
||||
file(GLOB lib_srcs ${IPP_IW_PATH}/src/*.c)
|
||||
file(GLOB lib_hdrs ${IPP_IW_PATH}/include/*.h ${IPP_IW_PATH}/include/iw/*.h ${IPP_IW_PATH}/include/iw++/*.hpp)
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
# Define the library target:
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
add_library(${IPP_IW_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
||||
|
||||
if(UNIX)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Wno-unused-function")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_target_properties(${IPP_IW_LIBRARY}
|
||||
PROPERTIES OUTPUT_NAME ${IPP_IW_LIBRARY}
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
COMPILE_PDB_NAME ${IPP_IW_LIBRARY}
|
||||
COMPILE_PDB_NAME_DEBUG "${IPP_IW_LIBRARY}${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(${IPP_IW_LIBRARY} PROPERTIES FOLDER "3rdparty")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(${IPP_IW_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
||||
endif()
|
||||
Vendored
-110
@@ -1,110 +0,0 @@
|
||||
#
|
||||
# The script downloads ICV package
|
||||
#
|
||||
# On return this will define:
|
||||
# OPENCV_ICV_PATH - path to unpacked downloaded package
|
||||
#
|
||||
|
||||
function(_icv_downloader)
|
||||
# Commit SHA in the opencv_3rdparty repo
|
||||
set(IPPICV_BINARIES_COMMIT "81a676001ca8075ada498583e4166079e5744668")
|
||||
# Define actual ICV versions
|
||||
if(APPLE)
|
||||
set(OPENCV_ICV_PACKAGE_NAME "ippicv_macosx_20151201.tgz")
|
||||
set(OPENCV_ICV_PACKAGE_HASH "4ff1fde9a7cfdfe7250bfcd8334e0f2f")
|
||||
set(OPENCV_ICV_PLATFORM "macosx")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "/ippicv_osx")
|
||||
elseif(UNIX)
|
||||
if(ANDROID AND NOT (ANDROID_ABI STREQUAL x86 OR ANDROID_ABI STREQUAL x86_64))
|
||||
return()
|
||||
endif()
|
||||
set(OPENCV_ICV_PACKAGE_NAME "ippicv_linux_20151201.tgz")
|
||||
set(OPENCV_ICV_PACKAGE_HASH "808b791a6eac9ed78d32a7666804320e")
|
||||
set(OPENCV_ICV_PLATFORM "linux")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "/ippicv_lnx")
|
||||
elseif(WIN32 AND NOT ARM)
|
||||
set(OPENCV_ICV_PACKAGE_NAME "ippicv_windows_20151201.zip")
|
||||
set(OPENCV_ICV_PACKAGE_HASH "04e81ce5d0e329c3fbc606ae32cad44d")
|
||||
set(OPENCV_ICV_PLATFORM "windows")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "/ippicv_win")
|
||||
else()
|
||||
return() # Not supported
|
||||
endif()
|
||||
|
||||
set(OPENCV_ICV_UNPACK_PATH "${CMAKE_BINARY_DIR}/3rdparty/ippicv")
|
||||
set(OPENCV_ICV_PATH "${OPENCV_ICV_UNPACK_PATH}${OPENCV_ICV_PACKAGE_SUBDIR}")
|
||||
|
||||
if(DEFINED OPENCV_ICV_PACKAGE_DOWNLOADED
|
||||
AND OPENCV_ICV_PACKAGE_DOWNLOADED STREQUAL OPENCV_ICV_PACKAGE_HASH
|
||||
AND EXISTS ${OPENCV_ICV_PATH})
|
||||
# Package has been downloaded and checked by the previous build
|
||||
set(OPENCV_ICV_PATH "${OPENCV_ICV_PATH}" PARENT_SCOPE)
|
||||
return()
|
||||
else()
|
||||
if(EXISTS ${OPENCV_ICV_UNPACK_PATH})
|
||||
message(STATUS "ICV: Removing previous unpacked package: ${OPENCV_ICV_UNPACK_PATH}")
|
||||
file(REMOVE_RECURSE ${OPENCV_ICV_UNPACK_PATH})
|
||||
endif()
|
||||
endif()
|
||||
unset(OPENCV_ICV_PACKAGE_DOWNLOADED CACHE)
|
||||
|
||||
set(OPENCV_ICV_PACKAGE_ARCHIVE "${CMAKE_CURRENT_LIST_DIR}/downloads/${OPENCV_ICV_PLATFORM}-${OPENCV_ICV_PACKAGE_HASH}/${OPENCV_ICV_PACKAGE_NAME}")
|
||||
get_filename_component(OPENCV_ICV_PACKAGE_ARCHIVE_DIR "${OPENCV_ICV_PACKAGE_ARCHIVE}" PATH)
|
||||
if(EXISTS "${OPENCV_ICV_PACKAGE_ARCHIVE}")
|
||||
file(MD5 "${OPENCV_ICV_PACKAGE_ARCHIVE}" archive_md5)
|
||||
if(NOT archive_md5 STREQUAL OPENCV_ICV_PACKAGE_HASH)
|
||||
message(WARNING "ICV: Local copy of ICV package has invalid MD5 hash: ${archive_md5} (expected: ${OPENCV_ICV_PACKAGE_HASH})")
|
||||
file(REMOVE "${OPENCV_ICV_PACKAGE_ARCHIVE}")
|
||||
file(REMOVE_RECURSE "${OPENCV_ICV_PACKAGE_ARCHIVE_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${OPENCV_ICV_PACKAGE_ARCHIVE}")
|
||||
if(NOT DEFINED OPENCV_ICV_URL)
|
||||
if(DEFINED ENV{OPENCV_ICV_URL})
|
||||
set(OPENCV_ICV_URL $ENV{OPENCV_ICV_URL})
|
||||
else()
|
||||
set(OPENCV_ICV_URL "https://raw.githubusercontent.com/opencv/opencv_3rdparty/${IPPICV_BINARIES_COMMIT}/ippicv")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${OPENCV_ICV_PACKAGE_ARCHIVE_DIR})
|
||||
message(STATUS "ICV: Downloading ${OPENCV_ICV_PACKAGE_NAME}...")
|
||||
file(DOWNLOAD "${OPENCV_ICV_URL}/${OPENCV_ICV_PACKAGE_NAME}" "${OPENCV_ICV_PACKAGE_ARCHIVE}"
|
||||
TIMEOUT 600 STATUS __status
|
||||
EXPECTED_MD5 ${OPENCV_ICV_PACKAGE_HASH})
|
||||
if(NOT __status EQUAL 0)
|
||||
message(FATAL_ERROR "ICV: Failed to download ICV package: ${OPENCV_ICV_PACKAGE_NAME}. Status=${__status}")
|
||||
else()
|
||||
# Don't remove this code, because EXPECTED_MD5 parameter doesn't fail "file(DOWNLOAD)" step
|
||||
# on wrong hash
|
||||
file(MD5 "${OPENCV_ICV_PACKAGE_ARCHIVE}" archive_md5)
|
||||
if(NOT archive_md5 STREQUAL OPENCV_ICV_PACKAGE_HASH)
|
||||
message(FATAL_ERROR "ICV: Downloaded copy of ICV package has invalid MD5 hash: ${archive_md5} (expected: ${OPENCV_ICV_PACKAGE_HASH})")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ocv_assert(EXISTS "${OPENCV_ICV_PACKAGE_ARCHIVE}")
|
||||
ocv_assert(NOT EXISTS "${OPENCV_ICV_UNPACK_PATH}")
|
||||
file(MAKE_DIRECTORY ${OPENCV_ICV_UNPACK_PATH})
|
||||
ocv_assert(EXISTS "${OPENCV_ICV_UNPACK_PATH}")
|
||||
|
||||
message(STATUS "ICV: Unpacking ${OPENCV_ICV_PACKAGE_NAME} to ${OPENCV_ICV_UNPACK_PATH}...")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xz "${OPENCV_ICV_PACKAGE_ARCHIVE}"
|
||||
WORKING_DIRECTORY "${OPENCV_ICV_UNPACK_PATH}"
|
||||
RESULT_VARIABLE __result)
|
||||
|
||||
if(NOT __result EQUAL 0)
|
||||
message(FATAL_ERROR "ICV: Failed to unpack ICV package from ${OPENCV_ICV_PACKAGE_ARCHIVE} to ${OPENCV_ICV_UNPACK_PATH} with error ${__result}")
|
||||
endif()
|
||||
|
||||
ocv_assert(EXISTS "${OPENCV_ICV_PATH}")
|
||||
|
||||
set(OPENCV_ICV_PACKAGE_DOWNLOADED "${OPENCV_ICV_PACKAGE_HASH}" CACHE INTERNAL "ICV package hash")
|
||||
|
||||
message(STATUS "ICV: Package successfully downloaded")
|
||||
set(OPENCV_ICV_PATH "${OPENCV_ICV_PATH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
_icv_downloader()
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
function(download_ippicv root_var)
|
||||
set(${root_var} "" PARENT_SCOPE)
|
||||
|
||||
# Commit SHA in the opencv_3rdparty repo
|
||||
set(IPPICV_COMMIT "a62e20676a60ee0ad6581e217fe7e4bada3b95db")
|
||||
# Define actual ICV versions
|
||||
if(APPLE)
|
||||
set(OPENCV_ICV_PLATFORM "macosx")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_mac")
|
||||
if(X86_64)
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_mac_intel64_20170418.tgz")
|
||||
set(OPENCV_ICV_HASH "0c25953c99dbb499ff502485a9356d8d")
|
||||
else()
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_mac_ia32_20170418.tgz")
|
||||
set(OPENCV_ICV_HASH "5f225948f3f64067c681293c098d50d8")
|
||||
endif()
|
||||
elseif((UNIX AND NOT ANDROID) OR (UNIX AND ANDROID_ABI MATCHES "x86"))
|
||||
set(OPENCV_ICV_PLATFORM "linux")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_lnx")
|
||||
if(X86_64)
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_lnx_intel64_20170418.tgz")
|
||||
set(OPENCV_ICV_HASH "87cbdeb627415d8e4bc811156289fa3a")
|
||||
else()
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_lnx_ia32_20170418.tgz")
|
||||
set(OPENCV_ICV_HASH "f2cece00d802d4dea86df52ed095257e")
|
||||
endif()
|
||||
elseif(WIN32 AND NOT ARM)
|
||||
set(OPENCV_ICV_PLATFORM "windows")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_win")
|
||||
if(X86_64)
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_win_intel64_20170418.zip")
|
||||
set(OPENCV_ICV_HASH "75060a0c662c0800f48995b7e9b085f6")
|
||||
else()
|
||||
set(OPENCV_ICV_NAME "ippicv_2017u2_win_ia32_20170418.zip")
|
||||
set(OPENCV_ICV_HASH "60fcf3ccd9a2ebc9e432ffb5cb91638b")
|
||||
endif()
|
||||
else()
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(THE_ROOT "${OpenCV_BINARY_DIR}/3rdparty/ippicv")
|
||||
ocv_download(FILENAME ${OPENCV_ICV_NAME}
|
||||
HASH ${OPENCV_ICV_HASH}
|
||||
URL
|
||||
"${OPENCV_IPPICV_URL}"
|
||||
"$ENV{OPENCV_IPPICV_URL}"
|
||||
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/${IPPICV_COMMIT}/ippicv/"
|
||||
DESTINATION_DIR "${THE_ROOT}"
|
||||
ID IPPICV
|
||||
STATUS res
|
||||
UNPACK RELATIVE_URL)
|
||||
|
||||
if(res)
|
||||
set(${root_var} "${THE_ROOT}/${OPENCV_ICV_PACKAGE_SUBDIR}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# CMake file for Intel ITT API. See root CMakeLists.txt
|
||||
#
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if(NOT ITT_LIBRARY)
|
||||
set(ITT_LIBRARY "ittnotify")
|
||||
endif()
|
||||
project(${ITT_LIBRARY} C)
|
||||
|
||||
if(NOT WIN32)
|
||||
include(CheckLibraryExists)
|
||||
if(COMMAND CHECK_LIBRARY_EXISTS)
|
||||
CHECK_LIBRARY_EXISTS(dl dlerror "" HAVE_DL_LIBRARY)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
set(ITT_INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
set(ITT_PUBLIC_HDRS
|
||||
include/ittnotify.h
|
||||
include/jitprofiling.h
|
||||
include/libittnotify.h
|
||||
include/llvm_jit_event_listener.hpp
|
||||
)
|
||||
set(ITT_PRIVATE_HDRS
|
||||
src/ittnotify/disable_warnings.h
|
||||
src/ittnotify/ittnotify_config.h
|
||||
src/ittnotify/ittnotify_static.h
|
||||
src/ittnotify/ittnotify_types.h
|
||||
)
|
||||
set(ITT_SRCS
|
||||
src/ittnotify/ittnotify_static.c
|
||||
src/ittnotify/jitprofiling.c
|
||||
)
|
||||
|
||||
add_library(${ITT_LIBRARY} STATIC ${ITT_SRCS} ${ITT_PUBLIC_HDRS} ${ITT_PRIVATE_HDRS})
|
||||
|
||||
if(NOT WIN32)
|
||||
if(HAVE_DL_LIBRARY)
|
||||
target_link_libraries(${ITT_LIBRARY} dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_target_properties(${ITT_LIBRARY} PROPERTIES
|
||||
OUTPUT_NAME ${ITT_LIBRARY}
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
COMPILE_PDB_NAME ${ITT_LIBRARY}
|
||||
COMPILE_PDB_NAME_DEBUG "${ITT_LIBRARY}${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
ocv_warnings_disable(CMAKE_C_FLAGS -Wundef -Wsign-compare)
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(${ITT_LIBRARY} PROPERTIES FOLDER "3rdparty")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(${ITT_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
||||
endif()
|
||||
+4123
File diff suppressed because it is too large
Load Diff
+694
@@ -0,0 +1,694 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#ifndef __JITPROFILING_H__
|
||||
#define __JITPROFILING_H__
|
||||
|
||||
/**
|
||||
* @brief JIT Profiling APIs
|
||||
*
|
||||
* The JIT Profiling API is used to report information about just-in-time
|
||||
* generated code that can be used by performance tools. The user inserts
|
||||
* calls in the code generator to report information before JIT-compiled
|
||||
* code goes to execution. This information is collected at runtime and used
|
||||
* by tools like Intel(R) VTune(TM) Amplifier to display performance metrics
|
||||
* associated with JIT-compiled code.
|
||||
*
|
||||
* These APIs can be used to\n
|
||||
* - **Profile trace-based and method-based JIT-compiled
|
||||
* code**. Some examples of environments that you can profile with these APIs:
|
||||
* dynamic JIT compilation of JavaScript code traces, JIT execution in OpenCL(TM)
|
||||
* software technology, Java/.NET managed execution environments, and custom
|
||||
* ISV JIT engines.
|
||||
* @code
|
||||
* #include <jitprofiling.h>
|
||||
*
|
||||
* if (iJIT_IsProfilingActive != iJIT_SAMPLING_ON) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* iJIT_Method_Load jmethod = {0};
|
||||
* jmethod.method_id = iJIT_GetNewMethodID();
|
||||
* jmethod.method_name = "method_name";
|
||||
* jmethod.class_file_name = "class_name";
|
||||
* jmethod.source_file_name = "source_file_name";
|
||||
* jmethod.method_load_address = code_addr;
|
||||
* jmethod.method_size = code_size;
|
||||
*
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&jmethod);
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);
|
||||
* @endcode
|
||||
*
|
||||
* * Expected behavior:
|
||||
* * If any iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED event overwrites an
|
||||
* already reported method, then such a method becomes invalid and its
|
||||
* memory region is treated as unloaded. VTune Amplifier displays the metrics
|
||||
* collected by the method until it is overwritten.
|
||||
* * If supplied line number information contains multiple source lines for
|
||||
* the same assembly instruction (code location), then VTune Amplifier picks up
|
||||
* the first line number.
|
||||
* * Dynamically generated code can be associated with a module name.
|
||||
* Use the iJIT_Method_Load_V2 structure.\n
|
||||
* Clarification of some cases:
|
||||
* * If you register a function with the same method ID multiple times,
|
||||
* specifying different module names, then the VTune Amplifier picks up
|
||||
* the module name registered first. If you want to distinguish the same
|
||||
* function between different JIT engines, supply different method IDs for
|
||||
* each function. Other symbolic information (for example, source file)
|
||||
* can be identical.
|
||||
*
|
||||
* - **Analyze split functions** (multiple joint or disjoint code regions
|
||||
* belonging to the same function) **including re-JIT**
|
||||
* with potential overlapping of code regions in time, which is common in
|
||||
* resource-limited environments.
|
||||
* @code
|
||||
* #include <jitprofiling.h>
|
||||
*
|
||||
* unsigned int method_id = iJIT_GetNewMethodID();
|
||||
*
|
||||
* iJIT_Method_Load a = {0};
|
||||
* a.method_id = method_id;
|
||||
* a.method_load_address = 0x100;
|
||||
* a.method_size = 0x20;
|
||||
*
|
||||
* iJIT_Method_Load b = {0};
|
||||
* b.method_id = method_id;
|
||||
* b.method_load_address = 0x200;
|
||||
* b.method_size = 0x30;
|
||||
*
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&a);
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&b);
|
||||
* @endcode
|
||||
*
|
||||
* * Expected behaviour:
|
||||
* * If a iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED event overwrites an
|
||||
* already reported method, then such a method becomes invalid and
|
||||
* its memory region is treated as unloaded.
|
||||
* * All code regions reported with the same method ID are considered as
|
||||
* belonging to the same method. Symbolic information (method name,
|
||||
* source file name) will be taken from the first notification, and all
|
||||
* subsequent notifications with the same method ID will be processed
|
||||
* only for line number table information. So, the VTune Amplifier will map
|
||||
* samples to a source line using the line number table from the current
|
||||
* notification while taking the source file name from the very first one.\n
|
||||
* Clarification of some cases:\n
|
||||
* * If you register a second code region with a different source file
|
||||
* name and the same method ID, then this information will be saved and
|
||||
* will not be considered as an extension of the first code region, but
|
||||
* VTune Amplifier will use the source file of the first code region and map
|
||||
* performance metrics incorrectly.
|
||||
* * If you register a second code region with the same source file as
|
||||
* for the first region and the same method ID, then the source file will be
|
||||
* discarded but VTune Amplifier will map metrics to the source file correctly.
|
||||
* * If you register a second code region with a null source file and
|
||||
* the same method ID, then provided line number info will be associated
|
||||
* with the source file of the first code region.
|
||||
*
|
||||
* - **Explore inline functions** including multi-level hierarchy of
|
||||
* nested inline methods which shows how performance metrics are distributed through them.
|
||||
* @code
|
||||
* #include <jitprofiling.h>
|
||||
*
|
||||
* // method_id parent_id
|
||||
* // [-- c --] 3000 2000
|
||||
* // [---- d -----] 2001 1000
|
||||
* // [---- b ----] 2000 1000
|
||||
* // [------------ a ----------------] 1000 n/a
|
||||
*
|
||||
* iJIT_Method_Load a = {0};
|
||||
* a.method_id = 1000;
|
||||
*
|
||||
* iJIT_Method_Inline_Load b = {0};
|
||||
* b.method_id = 2000;
|
||||
* b.parent_method_id = 1000;
|
||||
*
|
||||
* iJIT_Method_Inline_Load c = {0};
|
||||
* c.method_id = 3000;
|
||||
* c.parent_method_id = 2000;
|
||||
*
|
||||
* iJIT_Method_Inline_Load d = {0};
|
||||
* d.method_id = 2001;
|
||||
* d.parent_method_id = 1000;
|
||||
*
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&a);
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED, (void*)&b);
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED, (void*)&c);
|
||||
* iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED, (void*)&d);
|
||||
* @endcode
|
||||
*
|
||||
* * Requirements:
|
||||
* * Each inline (iJIT_Method_Inline_Load) method should be associated
|
||||
* with two method IDs: one for itself; one for its immediate parent.
|
||||
* * Address regions of inline methods of the same parent method cannot
|
||||
* overlap each other.
|
||||
* * Execution of the parent method must not be started until it and all
|
||||
* its inline methods are reported.
|
||||
* * Expected behaviour:
|
||||
* * In case of nested inline methods an order of
|
||||
* iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED events is not important.
|
||||
* * If any event overwrites either inline method or top parent method,
|
||||
* then the parent, including inline methods, becomes invalid and its memory
|
||||
* region is treated as unloaded.
|
||||
*
|
||||
* **Life time of allocated data**\n
|
||||
* The client sends an event notification to the agent with event-specific
|
||||
* data, which is a structure. The pointers in the structure refer to memory
|
||||
* allocated by the client, which responsible for releasing it. The pointers are
|
||||
* used by the iJIT_NotifyEvent method to copy client's data in a trace file,
|
||||
* and they are not used after the iJIT_NotifyEvent method returns.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup jitapi JIT Profiling
|
||||
* @ingroup internal
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enumerator for the types of notifications
|
||||
*/
|
||||
typedef enum iJIT_jvm_event
|
||||
{
|
||||
iJVM_EVENT_TYPE_SHUTDOWN = 2, /**<\brief Send this to shutdown the agent.
|
||||
* Use NULL for event data. */
|
||||
|
||||
iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED = 13, /**<\brief Send when dynamic code is
|
||||
* JIT compiled and loaded into
|
||||
* memory by the JIT engine, but
|
||||
* before the code is executed.
|
||||
* Use iJIT_Method_Load as event
|
||||
* data. */
|
||||
/** @cond exclude_from_documentation */
|
||||
iJVM_EVENT_TYPE_METHOD_UNLOAD_START, /**<\brief Send when compiled dynamic
|
||||
* code is being unloaded from memory.
|
||||
* Use iJIT_Method_Load as event data.*/
|
||||
/** @endcond */
|
||||
|
||||
iJVM_EVENT_TYPE_METHOD_UPDATE, /**<\brief Send to provide new content for
|
||||
* a previously reported dynamic code.
|
||||
* The previous content will be invalidated
|
||||
* starting from the time of the notification.
|
||||
* Use iJIT_Method_Load as event data but
|
||||
* required fields are following:
|
||||
* - method_id identify the code to update.
|
||||
* - method_load_address specify start address
|
||||
* within identified code range
|
||||
* where update should be started.
|
||||
* - method_size specify length of updated code
|
||||
* range. */
|
||||
|
||||
|
||||
iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED, /**<\brief Send when an inline dynamic
|
||||
* code is JIT compiled and loaded
|
||||
* into memory by the JIT engine,
|
||||
* but before the parent code region
|
||||
* starts executing.
|
||||
* Use iJIT_Method_Inline_Load as event data.*/
|
||||
|
||||
/** @cond exclude_from_documentation */
|
||||
iJVM_EVENT_TYPE_METHOD_UPDATE_V2,
|
||||
/** @endcond */
|
||||
|
||||
iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2 = 21, /**<\brief Send when a dynamic code is
|
||||
* JIT compiled and loaded into
|
||||
* memory by the JIT engine, but
|
||||
* before the code is executed.
|
||||
* Use iJIT_Method_Load_V2 as event data. */
|
||||
|
||||
iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V3 /**<\brief Send when a dynamic code is
|
||||
* JIT compiled and loaded into
|
||||
* memory by the JIT engine, but
|
||||
* before the code is executed.
|
||||
* Use iJIT_Method_Load_V3 as event data. */
|
||||
} iJIT_JVM_EVENT;
|
||||
|
||||
/**
|
||||
* @brief Enumerator for the agent's mode
|
||||
*/
|
||||
typedef enum _iJIT_IsProfilingActiveFlags
|
||||
{
|
||||
iJIT_NOTHING_RUNNING = 0x0000, /**<\brief The agent is not running;
|
||||
* iJIT_NotifyEvent calls will
|
||||
* not be processed. */
|
||||
iJIT_SAMPLING_ON = 0x0001, /**<\brief The agent is running and
|
||||
* ready to process notifications. */
|
||||
} iJIT_IsProfilingActiveFlags;
|
||||
|
||||
/**
|
||||
* @brief Description of a single entry in the line number information of a code region.
|
||||
* @details A table of line number entries gives information about how the reported code region
|
||||
* is mapped to source file.
|
||||
* Intel(R) VTune(TM) Amplifier uses line number information to attribute
|
||||
* the samples (virtual address) to a line number. \n
|
||||
* It is acceptable to report different code addresses for the same source line:
|
||||
* @code
|
||||
* Offset LineNumber
|
||||
* 1 2
|
||||
* 12 4
|
||||
* 15 2
|
||||
* 18 1
|
||||
* 21 30
|
||||
*
|
||||
* VTune Amplifier constructs the following table using the client data
|
||||
*
|
||||
* Code subrange Line number
|
||||
* 0-1 2
|
||||
* 1-12 4
|
||||
* 12-15 2
|
||||
* 15-18 1
|
||||
* 18-21 30
|
||||
* @endcode
|
||||
*/
|
||||
typedef struct _LineNumberInfo
|
||||
{
|
||||
unsigned int Offset; /**<\brief Offset from the begining of the code region. */
|
||||
unsigned int LineNumber; /**<\brief Matching source line number offset (from beginning of source file). */
|
||||
|
||||
} *pLineNumberInfo, LineNumberInfo;
|
||||
|
||||
/**
|
||||
* @brief Enumerator for the code architecture.
|
||||
*/
|
||||
typedef enum _iJIT_CodeArchitecture
|
||||
{
|
||||
iJIT_CA_NATIVE = 0, /**<\brief Native to the process architecture that is calling it. */
|
||||
|
||||
iJIT_CA_32, /**<\brief 32-bit machine code. */
|
||||
|
||||
iJIT_CA_64 /**<\brief 64-bit machine code. */
|
||||
|
||||
} iJIT_CodeArchitecture;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
/**
|
||||
* @brief Description of a JIT-compiled method
|
||||
* @details When you use the iJIT_Method_Load structure to describe
|
||||
* the JIT compiled method, use iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED
|
||||
* as an event type to report it.
|
||||
*/
|
||||
typedef struct _iJIT_Method_Load
|
||||
{
|
||||
unsigned int method_id; /**<\brief Unique method ID. Cannot be 0.
|
||||
* You must either use the API function
|
||||
* iJIT_GetNewMethodID to get a valid and unique
|
||||
* method ID, or else manage ID uniqueness
|
||||
* and correct range by yourself.\n
|
||||
* You must use the same method ID for all code
|
||||
* regions of the same method, otherwise different
|
||||
* method IDs specify different methods. */
|
||||
|
||||
char* method_name; /**<\brief The name of the method. It can be optionally
|
||||
* prefixed with its class name and appended with
|
||||
* its complete signature. Can't be NULL. */
|
||||
|
||||
void* method_load_address; /**<\brief The start virtual address of the method code
|
||||
* region. If NULL, data provided with
|
||||
* event are not accepted. */
|
||||
|
||||
unsigned int method_size; /**<\brief The code size of the method in memory.
|
||||
* If 0, then data provided with the event are not
|
||||
* accepted. */
|
||||
|
||||
unsigned int line_number_size; /**<\brief The number of entries in the line number
|
||||
* table.0 if none. */
|
||||
|
||||
pLineNumberInfo line_number_table; /**<\brief Pointer to the line numbers info
|
||||
* array. Can be NULL if
|
||||
* line_number_size is 0. See
|
||||
* LineNumberInfo Structure for a
|
||||
* description of a single entry in
|
||||
* the line number info array */
|
||||
|
||||
unsigned int class_id; /**<\brief This field is obsolete. */
|
||||
|
||||
char* class_file_name; /**<\brief Class name. Can be NULL.*/
|
||||
|
||||
char* source_file_name; /**<\brief Source file name. Can be NULL.*/
|
||||
|
||||
} *piJIT_Method_Load, iJIT_Method_Load;
|
||||
|
||||
/**
|
||||
* @brief Description of a JIT-compiled method
|
||||
* @details When you use the iJIT_Method_Load_V2 structure to describe
|
||||
* the JIT compiled method, use iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2
|
||||
* as an event type to report it.
|
||||
*/
|
||||
typedef struct _iJIT_Method_Load_V2
|
||||
{
|
||||
unsigned int method_id; /**<\brief Unique method ID. Cannot be 0.
|
||||
* You must either use the API function
|
||||
* iJIT_GetNewMethodID to get a valid and unique
|
||||
* method ID, or else manage ID uniqueness
|
||||
* and correct range by yourself.\n
|
||||
* You must use the same method ID for all code
|
||||
* regions of the same method, otherwise different
|
||||
* method IDs specify different methods. */
|
||||
|
||||
char* method_name; /**<\brief The name of the method. It can be optionally
|
||||
* prefixed with its class name and appended with
|
||||
* its complete signature. Can't be NULL. */
|
||||
|
||||
void* method_load_address; /**<\brief The start virtual address of the method code
|
||||
* region. If NULL, then data provided with the
|
||||
* event are not accepted. */
|
||||
|
||||
unsigned int method_size; /**<\brief The code size of the method in memory.
|
||||
* If 0, then data provided with the event are not
|
||||
* accepted. */
|
||||
|
||||
unsigned int line_number_size; /**<\brief The number of entries in the line number
|
||||
* table. 0 if none. */
|
||||
|
||||
pLineNumberInfo line_number_table; /**<\brief Pointer to the line numbers info
|
||||
* array. Can be NULL if
|
||||
* line_number_size is 0. See
|
||||
* LineNumberInfo Structure for a
|
||||
* description of a single entry in
|
||||
* the line number info array. */
|
||||
|
||||
char* class_file_name; /**<\brief Class name. Can be NULL. */
|
||||
|
||||
char* source_file_name; /**<\brief Source file name. Can be NULL. */
|
||||
|
||||
char* module_name; /**<\brief Module name. Can be NULL.
|
||||
The module name can be useful for distinguishing among
|
||||
different JIT engines. VTune Amplifier will display
|
||||
reported methods grouped by specific module. */
|
||||
|
||||
} *piJIT_Method_Load_V2, iJIT_Method_Load_V2;
|
||||
|
||||
/**
|
||||
* @brief Description of a JIT-compiled method
|
||||
* @details The iJIT_Method_Load_V3 structure is the same as iJIT_Method_Load_V2
|
||||
* with a newly introduced 'arch' field that specifies architecture of the code region.
|
||||
* When you use the iJIT_Method_Load_V3 structure to describe
|
||||
* the JIT compiled method, use iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V3
|
||||
* as an event type to report it.
|
||||
*/
|
||||
typedef struct _iJIT_Method_Load_V3
|
||||
{
|
||||
unsigned int method_id; /**<\brief Unique method ID. Cannot be 0.
|
||||
* You must either use the API function
|
||||
* iJIT_GetNewMethodID to get a valid and unique
|
||||
* method ID, or manage ID uniqueness
|
||||
* and correct range by yourself.\n
|
||||
* You must use the same method ID for all code
|
||||
* regions of the same method, otherwise they are
|
||||
* treated as regions of different methods. */
|
||||
|
||||
char* method_name; /**<\brief The name of the method. It can be optionally
|
||||
* prefixed with its class name and appended with
|
||||
* its complete signature. Cannot be NULL. */
|
||||
|
||||
void* method_load_address; /**<\brief The start virtual address of the method code
|
||||
* region. If NULL, then data provided with the
|
||||
* event are not accepted. */
|
||||
|
||||
unsigned int method_size; /**<\brief The code size of the method in memory.
|
||||
* If 0, then data provided with the event are not
|
||||
* accepted. */
|
||||
|
||||
unsigned int line_number_size; /**<\brief The number of entries in the line number
|
||||
* table. 0 if none. */
|
||||
|
||||
pLineNumberInfo line_number_table; /**<\brief Pointer to the line numbers info
|
||||
* array. Can be NULL if
|
||||
* line_number_size is 0. See
|
||||
* LineNumberInfo Structure for a
|
||||
* description of a single entry in
|
||||
* the line number info array. */
|
||||
|
||||
char* class_file_name; /**<\brief Class name. Can be NULL. */
|
||||
|
||||
char* source_file_name; /**<\brief Source file name. Can be NULL. */
|
||||
|
||||
char* module_name; /**<\brief Module name. Can be NULL.
|
||||
* The module name can be useful for distinguishing among
|
||||
* different JIT engines. VTune Amplifier will display
|
||||
* reported methods grouped by specific module. */
|
||||
|
||||
iJIT_CodeArchitecture module_arch; /**<\brief Architecture of the method's code region.
|
||||
* By default, it is the same as the process
|
||||
* architecture that is calling it.
|
||||
* For example, you can use it if your 32-bit JIT
|
||||
* engine generates 64-bit code.
|
||||
*
|
||||
* If JIT engine reports both 32-bit and 64-bit types
|
||||
* of methods then VTune Amplifier splits the methods
|
||||
* with the same module name but with different
|
||||
* architectures in two different modules. VTune Amplifier
|
||||
* modifies the original name provided with a 64-bit method
|
||||
* version by ending it with '(64)' */
|
||||
|
||||
} *piJIT_Method_Load_V3, iJIT_Method_Load_V3;
|
||||
|
||||
/**
|
||||
* @brief Description of an inline JIT-compiled method
|
||||
* @details When you use the_iJIT_Method_Inline_Load structure to describe
|
||||
* the JIT compiled method, use iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED
|
||||
* as an event type to report it.
|
||||
*/
|
||||
typedef struct _iJIT_Method_Inline_Load
|
||||
{
|
||||
unsigned int method_id; /**<\brief Unique method ID. Cannot be 0.
|
||||
* You must either use the API function
|
||||
* iJIT_GetNewMethodID to get a valid and unique
|
||||
* method ID, or else manage ID uniqueness
|
||||
* and correct range by yourself. */
|
||||
|
||||
unsigned int parent_method_id; /**<\brief Unique immediate parent's method ID.
|
||||
* Cannot be 0.
|
||||
* You must either use the API function
|
||||
* iJIT_GetNewMethodID to get a valid and unique
|
||||
* method ID, or else manage ID uniqueness
|
||||
* and correct range by yourself. */
|
||||
|
||||
char* method_name; /**<\brief The name of the method. It can be optionally
|
||||
* prefixed with its class name and appended with
|
||||
* its complete signature. Can't be NULL. */
|
||||
|
||||
void* method_load_address; /** <\brief The virtual address on which the method
|
||||
* is inlined. If NULL, then data provided with
|
||||
* the event are not accepted. */
|
||||
|
||||
unsigned int method_size; /**<\brief The code size of the method in memory.
|
||||
* If 0, then data provided with the event are not
|
||||
* accepted. */
|
||||
|
||||
unsigned int line_number_size; /**<\brief The number of entries in the line number
|
||||
* table. 0 if none. */
|
||||
|
||||
pLineNumberInfo line_number_table; /**<\brief Pointer to the line numbers info
|
||||
* array. Can be NULL if
|
||||
* line_number_size is 0. See
|
||||
* LineNumberInfo Structure for a
|
||||
* description of a single entry in
|
||||
* the line number info array */
|
||||
|
||||
char* class_file_name; /**<\brief Class name. Can be NULL.*/
|
||||
|
||||
char* source_file_name; /**<\brief Source file name. Can be NULL.*/
|
||||
|
||||
} *piJIT_Method_Inline_Load, iJIT_Method_Inline_Load;
|
||||
|
||||
/** @cond exclude_from_documentation */
|
||||
/**
|
||||
* @brief Description of a segment type
|
||||
* @details Use the segment type to specify a type of data supplied
|
||||
* with the iJVM_EVENT_TYPE_METHOD_UPDATE_V2 event to be applied to
|
||||
* a certain code trace.
|
||||
*/
|
||||
typedef enum _iJIT_SegmentType
|
||||
{
|
||||
iJIT_CT_UNKNOWN = 0,
|
||||
|
||||
iJIT_CT_CODE, /**<\brief Executable code. */
|
||||
|
||||
iJIT_CT_DATA, /**<\brief Data (not executable code).
|
||||
* VTune Amplifier uses the format string
|
||||
* (see iJIT_Method_Update) to represent
|
||||
* this data in the VTune Amplifier GUI */
|
||||
|
||||
iJIT_CT_KEEP, /**<\brief Use the previous markup for the trace.
|
||||
* Can be used for the following
|
||||
* iJVM_EVENT_TYPE_METHOD_UPDATE_V2 events,
|
||||
* if the type of the previously reported segment
|
||||
* type is the same. */
|
||||
iJIT_CT_EOF
|
||||
} iJIT_SegmentType;
|
||||
|
||||
/**
|
||||
* @brief Description of a dynamic update of the content within JIT-compiled method
|
||||
* @details The JIT engine may generate the methods that are updated at runtime
|
||||
* partially by mixed (data + executable code) content. When you use the iJIT_Method_Update
|
||||
* structure to describe the update of the content within a JIT-compiled method,
|
||||
* use iJVM_EVENT_TYPE_METHOD_UPDATE_V2 as an event type to report it.
|
||||
*
|
||||
* On the first Update event, VTune Amplifier copies the original code range reported by
|
||||
* the iJVM_EVENT_TYPE_METHOD_LOAD event, then modifies it with the supplied bytes and
|
||||
* adds the modified range to the original method. For next update events, VTune Amplifier
|
||||
* does the same but it uses the latest modified version of a code region for update.
|
||||
* Eventually, VTune Amplifier GUI displays multiple code ranges for the method reported by
|
||||
* the iJVM_EVENT_TYPE_METHOD_LOAD event.
|
||||
* Notes:
|
||||
* - Multiple update events with different types for the same trace are allowed
|
||||
* but they must be reported for the same code ranges.
|
||||
* Example,
|
||||
* @code
|
||||
* [-- data---] Allowed
|
||||
* [-- code --] Allowed
|
||||
* [code] Ignored
|
||||
* [-- data---] Allowed
|
||||
* [-- code --] Allowed
|
||||
* [------------ trace ---------]
|
||||
* @endcode
|
||||
* - The types of previously reported events can be changed but they must be reported
|
||||
* for the same code ranges.
|
||||
* Example,
|
||||
* @code
|
||||
* [-- data---] Allowed
|
||||
* [-- code --] Allowed
|
||||
* [-- data---] Allowed
|
||||
* [-- code --] Allowed
|
||||
* [------------ trace ---------]
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
typedef struct _iJIT_Method_Update
|
||||
{
|
||||
void* load_address; /**<\brief Start address of the update within a method */
|
||||
|
||||
unsigned int size; /**<\brief The update size */
|
||||
|
||||
iJIT_SegmentType type; /**<\brief Type of the update */
|
||||
|
||||
const char* data_format; /**<\brief C string that contains a format string
|
||||
* that follows the same specifications as format in printf.
|
||||
* The format string is used for iJIT_CT_CODE only
|
||||
* and cannot be NULL.
|
||||
* Format can be changed on the fly. */
|
||||
} *piJIT_Method_Update, iJIT_Method_Update;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/** @cond exclude_from_documentation */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef JITAPI_CDECL
|
||||
# if defined WIN32 || defined _WIN32
|
||||
# define JITAPI_CDECL __cdecl
|
||||
# else /* defined WIN32 || defined _WIN32 */
|
||||
# if defined _M_IX86 || defined __i386__
|
||||
# define JITAPI_CDECL __attribute__ ((cdecl))
|
||||
# else /* _M_IX86 || __i386__ */
|
||||
# define JITAPI_CDECL /* actual only on x86_64 platform */
|
||||
# endif /* _M_IX86 || __i386__ */
|
||||
# endif /* defined WIN32 || defined _WIN32 */
|
||||
#endif /* JITAPI_CDECL */
|
||||
|
||||
#define JITAPI JITAPI_CDECL
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Generates a new unique method ID.
|
||||
*
|
||||
* You must use this API to obtain unique and valid method IDs for methods or
|
||||
* traces reported to the agent if you don't have your own mechanism to generate
|
||||
* unique method IDs.
|
||||
*
|
||||
* @return a new unique method ID. When out of unique method IDs, this API
|
||||
* returns 0, which is not an accepted value.
|
||||
*/
|
||||
unsigned int JITAPI iJIT_GetNewMethodID(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the current mode of the agent.
|
||||
*
|
||||
* @return iJIT_SAMPLING_ON, indicating that agent is running, or
|
||||
* iJIT_NOTHING_RUNNING if no agent is running.
|
||||
*/
|
||||
iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive(void);
|
||||
|
||||
/**
|
||||
* @brief Reports infomation about JIT-compiled code to the agent.
|
||||
*
|
||||
* The reported information is used to attribute samples obtained from any
|
||||
* Intel(R) VTune(TM) Amplifier collector. This API needs to be called
|
||||
* after JIT compilation and before the first entry into the JIT-compiled
|
||||
* code.
|
||||
*
|
||||
* @param[in] event_type - type of the data sent to the agent
|
||||
* @param[in] EventSpecificData - pointer to event-specific data
|
||||
*
|
||||
* @returns 1 on success, otherwise 0.
|
||||
*/
|
||||
int JITAPI iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
/** @endcond */
|
||||
|
||||
/** @} jitapi group */
|
||||
|
||||
#endif /* __JITPROFILING_H__ */
|
||||
+1039
File diff suppressed because it is too large
Load Diff
+71
@@ -0,0 +1,71 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#ifndef _LIBITTNOTIFY_H_
|
||||
#define _LIBITTNOTIFY_H_
|
||||
|
||||
#ifndef __ITT_INTERNAL_INCLUDE
|
||||
# if defined WIN32 || defined _WIN32
|
||||
# pragma message("WARNING!!! Include file libittnotify.h is deprecated and should not be included anymore")
|
||||
# else /* WIN32 */
|
||||
# warning "Include file libittnotify.h is deprecated and should not be included anymore"
|
||||
# endif /* WIN32 */
|
||||
#endif /* __ITT_INTERNAL_INCLUDE */
|
||||
#include "legacy/ittnotify.h"
|
||||
|
||||
#endif /* _LIBITTNOTIFY_H_ */
|
||||
@@ -0,0 +1,241 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
/*
|
||||
* This file implements an interface bridge from Low-Level Virtual Machine
|
||||
* llvm::JITEventListener to Intel JIT Profiling API. It passes the function
|
||||
* and line information to the appropriate functions in the JIT profiling
|
||||
* interface so that any LLVM-based JIT engine can emit the JIT code
|
||||
* notifications that the profiler will receive.
|
||||
*
|
||||
* Usage model:
|
||||
*
|
||||
* 1. Register the listener implementation instance with the execution engine:
|
||||
*
|
||||
* #include <llvm_jit_event_listener.hpp>
|
||||
* ...
|
||||
* ExecutionEngine *TheExecutionEngine;
|
||||
* ...
|
||||
* TheExecutionEngine = EngineBuilder(TheModule).create();
|
||||
* ...
|
||||
* __itt_llvm_jit_event_listener jitListener;
|
||||
* TheExecutionEngine->RegisterJITEventListener(&jitListener);
|
||||
* ...
|
||||
*
|
||||
* 2. When compiling make sure to add the ITT API include directory to the
|
||||
* compiler include directories, ITT API library directory to the linker
|
||||
* library directories and link with jitprofling static library.
|
||||
*/
|
||||
|
||||
#ifndef __ITT_LLVM_JIT_EVENT_LISTENER_HPP__
|
||||
#define __ITT_LLVM_JIT_EVENT_LISTENER_HPP__
|
||||
|
||||
#include "jitprofiling.h"
|
||||
|
||||
#include <llvm/Function.h>
|
||||
#include <llvm/ExecutionEngine/JITEventListener.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/Analysis/DebugInfo.h>
|
||||
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
// Uncomment the line below to turn on logging to stderr
|
||||
#define JITPROFILING_DEBUG_ENABLE
|
||||
|
||||
// Some elementary logging support
|
||||
#ifdef JITPROFILING_DEBUG_ENABLE
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
static void _jit_debug(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
// Use the macro as JITDEBUG(("foo: %d", foo_val));
|
||||
#define JITDEBUG(x) \
|
||||
do { \
|
||||
_jit_debug("jit-listener: "); \
|
||||
_jit_debug x; \
|
||||
} \
|
||||
while (0)
|
||||
#else
|
||||
#define JITDEBUG(x)
|
||||
#endif
|
||||
|
||||
// LLVM JIT event listener, translates the notifications to the JIT profiling
|
||||
// API information.
|
||||
class __itt_llvm_jit_event_listener : public llvm::JITEventListener
|
||||
{
|
||||
public:
|
||||
__itt_llvm_jit_event_listener() {}
|
||||
|
||||
public:
|
||||
virtual void NotifyFunctionEmitted(const llvm::Function &F,
|
||||
void *Code, size_t Size, const EmittedFunctionDetails &Details)
|
||||
{
|
||||
std::string name = F.getName().str();
|
||||
JITDEBUG(("function jitted:\n"));
|
||||
JITDEBUG((" addr=0x%08x\n", (int)Code));
|
||||
JITDEBUG((" name=`%s'\n", name.c_str()));
|
||||
JITDEBUG((" code-size=%d\n", (int)Size));
|
||||
JITDEBUG((" line-infos-count=%d\n", Details.LineStarts.size()));
|
||||
|
||||
// The method must not be in the map - the entry must have been cleared
|
||||
// from the map in NotifyFreeingMachineCode in case of rejitting.
|
||||
assert(m_addr2MethodId.find(Code) == m_addr2MethodId.end());
|
||||
|
||||
int mid = iJIT_GetNewMethodID();
|
||||
m_addr2MethodId[Code] = mid;
|
||||
|
||||
iJIT_Method_Load mload;
|
||||
memset(&mload, 0, sizeof mload);
|
||||
mload.method_id = mid;
|
||||
|
||||
// Populate the method size and name information
|
||||
// TODO: The JIT profiling API should have members as const char pointers.
|
||||
mload.method_name = (char*)name.c_str();
|
||||
mload.method_load_address = Code;
|
||||
mload.method_size = (unsigned int)Size;
|
||||
|
||||
// Populate line information now.
|
||||
// From the JIT API documentation it is not quite clear whether the
|
||||
// line information can be given in ranges, so we'll populate it for
|
||||
// every byte of the function, hmm.
|
||||
std::string srcFilePath;
|
||||
std::vector<LineNumberInfo> lineInfos;
|
||||
char *addr = (char*)Code;
|
||||
char *lineAddr = addr; // Exclusive end point at which current
|
||||
// line info changes.
|
||||
const llvm::DebugLoc* loc = 0; // Current line info
|
||||
int lineIndex = -1; // Current index into the line info table
|
||||
for (int i = 0; i < Size; ++i, ++addr) {
|
||||
while (addr >= lineAddr) {
|
||||
if (lineIndex >= 0 && lineIndex < Details.LineStarts.size()) {
|
||||
loc = &Details.LineStarts[lineIndex].Loc;
|
||||
std::string p = getSrcFilePath(F.getContext(), *loc);
|
||||
assert(srcFilePath.empty() || p == srcFilePath);
|
||||
srcFilePath = p;
|
||||
} else {
|
||||
loc = NULL;
|
||||
}
|
||||
lineIndex++;
|
||||
if (lineIndex >= 0 && lineIndex < Details.LineStarts.size()) {
|
||||
lineAddr = (char*)Details.LineStarts[lineIndex].Address;
|
||||
} else {
|
||||
lineAddr = addr + Size;
|
||||
}
|
||||
}
|
||||
if (loc) {
|
||||
int line = loc->getLine();
|
||||
LineNumberInfo info = { i, line };
|
||||
lineInfos.push_back(info);
|
||||
JITDEBUG((" addr 0x%08x -> line %d\n", addr, line));
|
||||
}
|
||||
}
|
||||
if (!lineInfos.empty()) {
|
||||
mload.line_number_size = lineInfos.size();
|
||||
JITDEBUG((" translated to %d line infos to JIT", (int)lineInfos.size()));
|
||||
mload.line_number_table = &lineInfos[0];
|
||||
mload.source_file_name = (char*)srcFilePath.c_str();
|
||||
}
|
||||
|
||||
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &mload);
|
||||
}
|
||||
|
||||
virtual void NotifyFreeingMachineCode(void *OldPtr)
|
||||
{
|
||||
JITDEBUG(("function unjitted\n"));
|
||||
JITDEBUG((" addr=0x%08x\n", (int)OldPtr));
|
||||
Addr2MethodId::iterator it = m_addr2MethodId.find(OldPtr);
|
||||
assert(it != m_addr2MethodId.end());
|
||||
iJIT_Method_Id mid = { it->second };
|
||||
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, &mid);
|
||||
m_addr2MethodId.erase(it);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getSrcFilePath(const llvm::LLVMContext& ctx, const llvm::DebugLoc& loc)
|
||||
{
|
||||
llvm::MDNode* node = loc.getAsMDNode(ctx);
|
||||
llvm::DILocation srcLoc(node);
|
||||
return srcLoc.getDirectory().str() + "/" + srcLoc.getFilename().str();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Don't copy
|
||||
__itt_llvm_jit_event_listener(const __itt_llvm_jit_event_listener&);
|
||||
__itt_llvm_jit_event_listener& operator=(const __itt_llvm_jit_event_listener&);
|
||||
|
||||
private:
|
||||
typedef std::vector<LineNumberInfo> LineInfoList;
|
||||
|
||||
// The method unload notification in VTune JIT profiling API takes the
|
||||
// method ID, not method address so have to maintain the mapping. Is
|
||||
// there a more efficient and simple way to do this like attaching the
|
||||
// method ID information somehow to the LLVM function instance?
|
||||
//
|
||||
// TODO: It would be more convenient for the JIT API to take the method
|
||||
// address, not method ID.
|
||||
typedef std::map<const void*, int> Addr2MethodId;
|
||||
Addr2MethodId m_addr2MethodId;
|
||||
};
|
||||
|
||||
#endif // Header guard
|
||||
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2011, Intel Corporation
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
• Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
• Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
• Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
The GNU General Public License (GPL)
|
||||
Version 2, June 1991
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Preamble
|
||||
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
|
||||
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
|
||||
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
|
||||
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
|
||||
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
|
||||
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
|
||||
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
|
||||
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
|
||||
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
|
||||
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
|
||||
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
|
||||
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
|
||||
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
|
||||
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
|
||||
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
|
||||
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
|
||||
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
|
||||
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
|
||||
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
|
||||
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||
NO WARRANTY
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
END OF TERMS AND CONDITIONS
|
||||
How to Apply These Terms to Your New Programs
|
||||
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
One line to give the program's name and a brief idea of what it does.
|
||||
Copyright (C) <year> <name of author>
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
|
||||
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
|
||||
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
signature of Ty Coon, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
|
||||
@@ -0,0 +1,77 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#include "ittnotify_config.h"
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
|
||||
#pragma warning (disable: 593) /* parameter "XXXX" was set but never used */
|
||||
#pragma warning (disable: 344) /* typedef name has already been declared (with same type) */
|
||||
#pragma warning (disable: 174) /* expression has no effect */
|
||||
#pragma warning (disable: 4127) /* conditional expression is constant */
|
||||
#pragma warning (disable: 4306) /* conversion from '?' to '?' of greater size */
|
||||
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if defined __INTEL_COMPILER
|
||||
|
||||
#pragma warning (disable: 869) /* parameter "XXXXX" was never referenced */
|
||||
#pragma warning (disable: 1418) /* external function definition with no prior declaration */
|
||||
#pragma warning (disable: 1419) /* external declaration in primary source file */
|
||||
|
||||
#endif /* __INTEL_COMPILER */
|
||||
+616
@@ -0,0 +1,616 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
#ifndef _ITTNOTIFY_CONFIG_H_
|
||||
#define _ITTNOTIFY_CONFIG_H_
|
||||
|
||||
/** @cond exclude_from_documentation */
|
||||
#ifndef ITT_OS_WIN
|
||||
# define ITT_OS_WIN 1
|
||||
#endif /* ITT_OS_WIN */
|
||||
|
||||
#ifndef ITT_OS_LINUX
|
||||
# define ITT_OS_LINUX 2
|
||||
#endif /* ITT_OS_LINUX */
|
||||
|
||||
#ifndef ITT_OS_MAC
|
||||
# define ITT_OS_MAC 3
|
||||
#endif /* ITT_OS_MAC */
|
||||
|
||||
#ifndef ITT_OS_FREEBSD
|
||||
# define ITT_OS_FREEBSD 4
|
||||
#endif /* ITT_OS_FREEBSD */
|
||||
|
||||
#ifndef ITT_OS
|
||||
# if defined WIN32 || defined _WIN32
|
||||
# define ITT_OS ITT_OS_WIN
|
||||
# elif defined( __APPLE__ ) && defined( __MACH__ )
|
||||
# define ITT_OS ITT_OS_MAC
|
||||
# elif defined( __FreeBSD__ )
|
||||
# define ITT_OS ITT_OS_FREEBSD
|
||||
# else
|
||||
# define ITT_OS ITT_OS_LINUX
|
||||
# endif
|
||||
#endif /* ITT_OS */
|
||||
|
||||
#ifndef ITT_PLATFORM_WIN
|
||||
# define ITT_PLATFORM_WIN 1
|
||||
#endif /* ITT_PLATFORM_WIN */
|
||||
|
||||
#ifndef ITT_PLATFORM_POSIX
|
||||
# define ITT_PLATFORM_POSIX 2
|
||||
#endif /* ITT_PLATFORM_POSIX */
|
||||
|
||||
#ifndef ITT_PLATFORM_MAC
|
||||
# define ITT_PLATFORM_MAC 3
|
||||
#endif /* ITT_PLATFORM_MAC */
|
||||
|
||||
#ifndef ITT_PLATFORM_FREEBSD
|
||||
# define ITT_PLATFORM_FREEBSD 4
|
||||
#endif /* ITT_PLATFORM_FREEBSD */
|
||||
|
||||
#ifndef ITT_PLATFORM
|
||||
# if ITT_OS==ITT_OS_WIN
|
||||
# define ITT_PLATFORM ITT_PLATFORM_WIN
|
||||
# elif ITT_OS==ITT_OS_MAC
|
||||
# define ITT_PLATFORM ITT_PLATFORM_MAC
|
||||
# elif ITT_OS==ITT_OS_FREEBSD
|
||||
# define ITT_PLATFORM ITT_PLATFORM_FREEBSD
|
||||
# else
|
||||
# define ITT_PLATFORM ITT_PLATFORM_POSIX
|
||||
# endif
|
||||
#endif /* ITT_PLATFORM */
|
||||
|
||||
#if defined(_UNICODE) && !defined(UNICODE)
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#include <tchar.h>
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#include <stdint.h>
|
||||
#if defined(UNICODE) || defined(_UNICODE)
|
||||
#include <wchar.h>
|
||||
#endif /* UNICODE || _UNICODE */
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#ifndef ITTAPI_CDECL
|
||||
# if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
# define ITTAPI_CDECL __cdecl
|
||||
# else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
# if defined _M_IX86 || defined __i386__
|
||||
# define ITTAPI_CDECL __attribute__ ((cdecl))
|
||||
# else /* _M_IX86 || __i386__ */
|
||||
# define ITTAPI_CDECL /* actual only on x86 platform */
|
||||
# endif /* _M_IX86 || __i386__ */
|
||||
# endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* ITTAPI_CDECL */
|
||||
|
||||
#ifndef STDCALL
|
||||
# if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
# define STDCALL __stdcall
|
||||
# else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
# if defined _M_IX86 || defined __i386__
|
||||
# define STDCALL __attribute__ ((stdcall))
|
||||
# else /* _M_IX86 || __i386__ */
|
||||
# define STDCALL /* supported only on x86 platform */
|
||||
# endif /* _M_IX86 || __i386__ */
|
||||
# endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* STDCALL */
|
||||
|
||||
#define ITTAPI ITTAPI_CDECL
|
||||
#define LIBITTAPI ITTAPI_CDECL
|
||||
|
||||
/* TODO: Temporary for compatibility! */
|
||||
#define ITTAPI_CALL ITTAPI_CDECL
|
||||
#define LIBITTAPI_CALL ITTAPI_CDECL
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
/* use __forceinline (VC++ specific) */
|
||||
#define ITT_INLINE __forceinline
|
||||
#define ITT_INLINE_ATTRIBUTE /* nothing */
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
/*
|
||||
* Generally, functions are not inlined unless optimization is specified.
|
||||
* For functions declared inline, this attribute inlines the function even
|
||||
* if no optimization level was specified.
|
||||
*/
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define ITT_INLINE static
|
||||
#define ITT_INLINE_ATTRIBUTE __attribute__((unused))
|
||||
#else /* __STRICT_ANSI__ */
|
||||
#define ITT_INLINE static inline
|
||||
#define ITT_INLINE_ATTRIBUTE __attribute__((always_inline, unused))
|
||||
#endif /* __STRICT_ANSI__ */
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
/** @endcond */
|
||||
|
||||
#ifndef ITT_ARCH_IA32
|
||||
# define ITT_ARCH_IA32 1
|
||||
#endif /* ITT_ARCH_IA32 */
|
||||
|
||||
#ifndef ITT_ARCH_IA32E
|
||||
# define ITT_ARCH_IA32E 2
|
||||
#endif /* ITT_ARCH_IA32E */
|
||||
|
||||
#ifndef ITT_ARCH_ARM
|
||||
# define ITT_ARCH_ARM 4
|
||||
#endif /* ITT_ARCH_ARM */
|
||||
|
||||
#ifndef ITT_ARCH_PPC64
|
||||
# define ITT_ARCH_PPC64 5
|
||||
#endif /* ITT_ARCH_PPC64 */
|
||||
|
||||
#ifndef ITT_ARCH
|
||||
# if defined _M_IX86 || defined __i386__
|
||||
# define ITT_ARCH ITT_ARCH_IA32
|
||||
# elif defined _M_X64 || defined _M_AMD64 || defined __x86_64__
|
||||
# define ITT_ARCH ITT_ARCH_IA32E
|
||||
# elif defined _M_IA64 || defined __ia64__
|
||||
# define ITT_ARCH ITT_ARCH_IA64
|
||||
# elif defined _M_ARM || defined __arm__
|
||||
# define ITT_ARCH ITT_ARCH_ARM
|
||||
# elif defined __powerpc64__
|
||||
# define ITT_ARCH ITT_ARCH_PPC64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define ITT_EXTERN_C extern "C"
|
||||
# define ITT_EXTERN_C_BEGIN extern "C" {
|
||||
# define ITT_EXTERN_C_END }
|
||||
#else
|
||||
# define ITT_EXTERN_C /* nothing */
|
||||
# define ITT_EXTERN_C_BEGIN /* nothing */
|
||||
# define ITT_EXTERN_C_END /* nothing */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define ITT_TO_STR_AUX(x) #x
|
||||
#define ITT_TO_STR(x) ITT_TO_STR_AUX(x)
|
||||
|
||||
#define __ITT_BUILD_ASSERT(expr, suffix) do { \
|
||||
static char __itt_build_check_##suffix[(expr) ? 1 : -1]; \
|
||||
__itt_build_check_##suffix[0] = 0; \
|
||||
} while(0)
|
||||
#define _ITT_BUILD_ASSERT(expr, suffix) __ITT_BUILD_ASSERT((expr), suffix)
|
||||
#define ITT_BUILD_ASSERT(expr) _ITT_BUILD_ASSERT((expr), __LINE__)
|
||||
|
||||
#define ITT_MAGIC { 0xED, 0xAB, 0xAB, 0xEC, 0x0D, 0xEE, 0xDA, 0x30 }
|
||||
|
||||
/* Replace with snapshot date YYYYMMDD for promotion build. */
|
||||
#define API_VERSION_BUILD 20151119
|
||||
|
||||
#ifndef API_VERSION_NUM
|
||||
#define API_VERSION_NUM 0.0.0
|
||||
#endif /* API_VERSION_NUM */
|
||||
|
||||
#define API_VERSION "ITT-API-Version " ITT_TO_STR(API_VERSION_NUM) \
|
||||
" (" ITT_TO_STR(API_VERSION_BUILD) ")"
|
||||
|
||||
/* OS communication functions */
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#include <windows.h>
|
||||
typedef HMODULE lib_t;
|
||||
typedef DWORD TIDT;
|
||||
typedef CRITICAL_SECTION mutex_t;
|
||||
#define MUTEX_INITIALIZER { 0 }
|
||||
#define strong_alias(name, aliasname) /* empty for Windows */
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#include <dlfcn.h>
|
||||
#if defined(UNICODE) || defined(_UNICODE)
|
||||
#include <wchar.h>
|
||||
#endif /* UNICODE */
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1 /* need for PTHREAD_MUTEX_RECURSIVE */
|
||||
#endif /* _GNU_SOURCE */
|
||||
#ifndef __USE_UNIX98
|
||||
#define __USE_UNIX98 1 /* need for PTHREAD_MUTEX_RECURSIVE, on SLES11.1 with gcc 4.3.4 wherein pthread.h missing dependency on __USE_XOPEN2K8 */
|
||||
#endif /*__USE_UNIX98*/
|
||||
#include <pthread.h>
|
||||
typedef void* lib_t;
|
||||
typedef pthread_t TIDT;
|
||||
typedef pthread_mutex_t mutex_t;
|
||||
#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
|
||||
#define _strong_alias(name, aliasname) \
|
||||
extern __typeof (name) aliasname __attribute__ ((alias (#name)));
|
||||
#define strong_alias(name, aliasname) _strong_alias(name, aliasname)
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#define __itt_get_proc(lib, name) GetProcAddress(lib, name)
|
||||
#define __itt_mutex_init(mutex) InitializeCriticalSection(mutex)
|
||||
#define __itt_mutex_lock(mutex) EnterCriticalSection(mutex)
|
||||
#define __itt_mutex_unlock(mutex) LeaveCriticalSection(mutex)
|
||||
#define __itt_load_lib(name) LoadLibraryA(name)
|
||||
#define __itt_unload_lib(handle) FreeLibrary(handle)
|
||||
#define __itt_system_error() (int)GetLastError()
|
||||
#define __itt_fstrcmp(s1, s2) lstrcmpA(s1, s2)
|
||||
#define __itt_fstrnlen(s, l) strnlen_s(s, l)
|
||||
#define __itt_fstrcpyn(s1, b, s2, l) strncpy_s(s1, b, s2, l)
|
||||
#define __itt_fstrdup(s) _strdup(s)
|
||||
#define __itt_thread_id() GetCurrentThreadId()
|
||||
#define __itt_thread_yield() SwitchToThread()
|
||||
#ifndef ITT_SIMPLE_INIT
|
||||
ITT_INLINE long
|
||||
__itt_interlocked_increment(volatile long* ptr) ITT_INLINE_ATTRIBUTE;
|
||||
ITT_INLINE long __itt_interlocked_increment(volatile long* ptr)
|
||||
{
|
||||
return InterlockedIncrement(ptr);
|
||||
}
|
||||
#endif /* ITT_SIMPLE_INIT */
|
||||
|
||||
#define DL_SYMBOLS (1)
|
||||
#define PTHREAD_SYMBOLS (1)
|
||||
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
#define __itt_get_proc(lib, name) dlsym(lib, name)
|
||||
#define __itt_mutex_init(mutex) {\
|
||||
pthread_mutexattr_t mutex_attr; \
|
||||
int error_code = pthread_mutexattr_init(&mutex_attr); \
|
||||
if (error_code) \
|
||||
__itt_report_error(__itt_error_system, "pthread_mutexattr_init", \
|
||||
error_code); \
|
||||
error_code = pthread_mutexattr_settype(&mutex_attr, \
|
||||
PTHREAD_MUTEX_RECURSIVE); \
|
||||
if (error_code) \
|
||||
__itt_report_error(__itt_error_system, "pthread_mutexattr_settype", \
|
||||
error_code); \
|
||||
error_code = pthread_mutex_init(mutex, &mutex_attr); \
|
||||
if (error_code) \
|
||||
__itt_report_error(__itt_error_system, "pthread_mutex_init", \
|
||||
error_code); \
|
||||
error_code = pthread_mutexattr_destroy(&mutex_attr); \
|
||||
if (error_code) \
|
||||
__itt_report_error(__itt_error_system, "pthread_mutexattr_destroy", \
|
||||
error_code); \
|
||||
}
|
||||
#define __itt_mutex_lock(mutex) pthread_mutex_lock(mutex)
|
||||
#define __itt_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
|
||||
#define __itt_load_lib(name) dlopen(name, RTLD_LAZY)
|
||||
#define __itt_unload_lib(handle) dlclose(handle)
|
||||
#define __itt_system_error() errno
|
||||
#define __itt_fstrcmp(s1, s2) strcmp(s1, s2)
|
||||
|
||||
/* makes customer code define safe APIs for SDL_STRNLEN_S and SDL_STRNCPY_S */
|
||||
#ifdef SDL_STRNLEN_S
|
||||
#define __itt_fstrnlen(s, l) SDL_STRNLEN_S(s, l)
|
||||
#else
|
||||
#define __itt_fstrnlen(s, l) strlen(s)
|
||||
#endif /* SDL_STRNLEN_S */
|
||||
#ifdef SDL_STRNCPY_S
|
||||
#define __itt_fstrcpyn(s1, b, s2, l) SDL_STRNCPY_S(s1, b, s2, l)
|
||||
#else
|
||||
#define __itt_fstrcpyn(s1, b, s2, l) strncpy(s1, s2, l)
|
||||
#endif /* SDL_STRNCPY_S */
|
||||
|
||||
#define __itt_fstrdup(s) strdup(s)
|
||||
#define __itt_thread_id() pthread_self()
|
||||
#define __itt_thread_yield() sched_yield()
|
||||
#if ITT_ARCH==ITT_ARCH_IA64
|
||||
#ifdef __INTEL_COMPILER
|
||||
#define __TBB_machine_fetchadd4(addr, val) __fetchadd4_acq((void *)addr, val)
|
||||
#else /* __INTEL_COMPILER */
|
||||
/* TODO: Add Support for not Intel compilers for IA-64 architecture */
|
||||
#endif /* __INTEL_COMPILER */
|
||||
#elif ITT_ARCH==ITT_ARCH_IA32 || ITT_ARCH==ITT_ARCH_IA32E /* ITT_ARCH!=ITT_ARCH_IA64 */
|
||||
ITT_INLINE long
|
||||
__TBB_machine_fetchadd4(volatile void* ptr, long addend) ITT_INLINE_ATTRIBUTE;
|
||||
ITT_INLINE long __TBB_machine_fetchadd4(volatile void* ptr, long addend)
|
||||
{
|
||||
long result;
|
||||
__asm__ __volatile__("lock\nxadd %0,%1"
|
||||
: "=r"(result),"=m"(*(int*)ptr)
|
||||
: "0"(addend), "m"(*(int*)ptr)
|
||||
: "memory");
|
||||
return result;
|
||||
}
|
||||
#elif ITT_ARCH==ITT_ARCH_ARM || ITT_ARCH==ITT_ARCH_PPC64
|
||||
#define __TBB_machine_fetchadd4(addr, val) __sync_fetch_and_add(addr, val)
|
||||
#endif /* ITT_ARCH==ITT_ARCH_IA64 */
|
||||
#ifndef ITT_SIMPLE_INIT
|
||||
ITT_INLINE long
|
||||
__itt_interlocked_increment(volatile long* ptr) ITT_INLINE_ATTRIBUTE;
|
||||
ITT_INLINE long __itt_interlocked_increment(volatile long* ptr)
|
||||
{
|
||||
return __TBB_machine_fetchadd4(ptr, 1) + 1L;
|
||||
}
|
||||
#endif /* ITT_SIMPLE_INIT */
|
||||
|
||||
void* dlopen(const char*, int) __attribute__((weak));
|
||||
void* dlsym(void*, const char*) __attribute__((weak));
|
||||
int dlclose(void*) __attribute__((weak));
|
||||
#define DL_SYMBOLS (dlopen && dlsym && dlclose)
|
||||
|
||||
int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*) __attribute__((weak));
|
||||
int pthread_mutex_lock(pthread_mutex_t*) __attribute__((weak));
|
||||
int pthread_mutex_unlock(pthread_mutex_t*) __attribute__((weak));
|
||||
int pthread_mutex_destroy(pthread_mutex_t*) __attribute__((weak));
|
||||
int pthread_mutexattr_init(pthread_mutexattr_t*) __attribute__((weak));
|
||||
int pthread_mutexattr_settype(pthread_mutexattr_t*, int) __attribute__((weak));
|
||||
int pthread_mutexattr_destroy(pthread_mutexattr_t*) __attribute__((weak));
|
||||
pthread_t pthread_self(void) __attribute__((weak));
|
||||
#define PTHREAD_SYMBOLS (pthread_mutex_init && pthread_mutex_lock && pthread_mutex_unlock && pthread_mutex_destroy && pthread_mutexattr_init && pthread_mutexattr_settype && pthread_mutexattr_destroy && pthread_self)
|
||||
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
typedef enum {
|
||||
__itt_collection_normal = 0,
|
||||
__itt_collection_paused = 1
|
||||
} __itt_collection_state;
|
||||
|
||||
typedef enum {
|
||||
__itt_thread_normal = 0,
|
||||
__itt_thread_ignored = 1
|
||||
} __itt_thread_state;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
typedef struct ___itt_thread_info
|
||||
{
|
||||
const char* nameA; /*!< Copy of original name in ASCII. */
|
||||
#if defined(UNICODE) || defined(_UNICODE)
|
||||
const wchar_t* nameW; /*!< Copy of original name in UNICODE. */
|
||||
#else /* UNICODE || _UNICODE */
|
||||
void* nameW;
|
||||
#endif /* UNICODE || _UNICODE */
|
||||
TIDT tid;
|
||||
__itt_thread_state state; /*!< Thread state (paused or normal) */
|
||||
int extra1; /*!< Reserved to the runtime */
|
||||
void* extra2; /*!< Reserved to the runtime */
|
||||
struct ___itt_thread_info* next;
|
||||
} __itt_thread_info;
|
||||
|
||||
#include "ittnotify_types.h" /* For __itt_group_id definition */
|
||||
|
||||
typedef struct ___itt_api_info_20101001
|
||||
{
|
||||
const char* name;
|
||||
void** func_ptr;
|
||||
void* init_func;
|
||||
__itt_group_id group;
|
||||
} __itt_api_info_20101001;
|
||||
|
||||
typedef struct ___itt_api_info
|
||||
{
|
||||
const char* name;
|
||||
void** func_ptr;
|
||||
void* init_func;
|
||||
void* null_func;
|
||||
__itt_group_id group;
|
||||
} __itt_api_info;
|
||||
|
||||
typedef struct __itt_counter_info
|
||||
{
|
||||
const char* nameA; /*!< Copy of original name in ASCII. */
|
||||
#if defined(UNICODE) || defined(_UNICODE)
|
||||
const wchar_t* nameW; /*!< Copy of original name in UNICODE. */
|
||||
#else /* UNICODE || _UNICODE */
|
||||
void* nameW;
|
||||
#endif /* UNICODE || _UNICODE */
|
||||
const char* domainA; /*!< Copy of original name in ASCII. */
|
||||
#if defined(UNICODE) || defined(_UNICODE)
|
||||
const wchar_t* domainW; /*!< Copy of original name in UNICODE. */
|
||||
#else /* UNICODE || _UNICODE */
|
||||
void* domainW;
|
||||
#endif /* UNICODE || _UNICODE */
|
||||
int type;
|
||||
long index;
|
||||
int extra1; /*!< Reserved to the runtime */
|
||||
void* extra2; /*!< Reserved to the runtime */
|
||||
struct __itt_counter_info* next;
|
||||
} __itt_counter_info_t;
|
||||
|
||||
struct ___itt_domain;
|
||||
struct ___itt_string_handle;
|
||||
|
||||
typedef struct ___itt_global
|
||||
{
|
||||
unsigned char magic[8];
|
||||
unsigned long version_major;
|
||||
unsigned long version_minor;
|
||||
unsigned long version_build;
|
||||
volatile long api_initialized;
|
||||
volatile long mutex_initialized;
|
||||
volatile long atomic_counter;
|
||||
mutex_t mutex;
|
||||
lib_t lib;
|
||||
void* error_handler;
|
||||
const char** dll_path_ptr;
|
||||
__itt_api_info* api_list_ptr;
|
||||
struct ___itt_global* next;
|
||||
/* Joinable structures below */
|
||||
__itt_thread_info* thread_list;
|
||||
struct ___itt_domain* domain_list;
|
||||
struct ___itt_string_handle* string_list;
|
||||
__itt_collection_state state;
|
||||
__itt_counter_info_t* counter_list;
|
||||
} __itt_global;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define NEW_THREAD_INFO_W(gptr,h,h_tail,t,s,n) { \
|
||||
h = (__itt_thread_info*)malloc(sizeof(__itt_thread_info)); \
|
||||
if (h != NULL) { \
|
||||
h->tid = t; \
|
||||
h->nameA = NULL; \
|
||||
h->nameW = n ? _wcsdup(n) : NULL; \
|
||||
h->state = s; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->thread_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_THREAD_INFO_A(gptr,h,h_tail,t,s,n) { \
|
||||
h = (__itt_thread_info*)malloc(sizeof(__itt_thread_info)); \
|
||||
if (h != NULL) { \
|
||||
h->tid = t; \
|
||||
h->nameA = n ? __itt_fstrdup(n) : NULL; \
|
||||
h->nameW = NULL; \
|
||||
h->state = s; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->thread_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_DOMAIN_W(gptr,h,h_tail,name) { \
|
||||
h = (__itt_domain*)malloc(sizeof(__itt_domain)); \
|
||||
if (h != NULL) { \
|
||||
h->flags = 1; /* domain is enabled by default */ \
|
||||
h->nameA = NULL; \
|
||||
h->nameW = name ? _wcsdup(name) : NULL; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->domain_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_DOMAIN_A(gptr,h,h_tail,name) { \
|
||||
h = (__itt_domain*)malloc(sizeof(__itt_domain)); \
|
||||
if (h != NULL) { \
|
||||
h->flags = 1; /* domain is enabled by default */ \
|
||||
h->nameA = name ? __itt_fstrdup(name) : NULL; \
|
||||
h->nameW = NULL; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->domain_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_STRING_HANDLE_W(gptr,h,h_tail,name) { \
|
||||
h = (__itt_string_handle*)malloc(sizeof(__itt_string_handle)); \
|
||||
if (h != NULL) { \
|
||||
h->strA = NULL; \
|
||||
h->strW = name ? _wcsdup(name) : NULL; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->string_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_STRING_HANDLE_A(gptr,h,h_tail,name) { \
|
||||
h = (__itt_string_handle*)malloc(sizeof(__itt_string_handle)); \
|
||||
if (h != NULL) { \
|
||||
h->strA = name ? __itt_fstrdup(name) : NULL; \
|
||||
h->strW = NULL; \
|
||||
h->extra1 = 0; /* reserved */ \
|
||||
h->extra2 = NULL; /* reserved */ \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->string_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_COUNTER_W(gptr,h,h_tail,name,domain,type) { \
|
||||
h = (__itt_counter_info_t*)malloc(sizeof(__itt_counter_info_t)); \
|
||||
if (h != NULL) { \
|
||||
h->nameA = NULL; \
|
||||
h->nameW = name ? _wcsdup(name) : NULL; \
|
||||
h->domainA = NULL; \
|
||||
h->domainW = name ? _wcsdup(domain) : NULL; \
|
||||
h->type = type; \
|
||||
h->index = 0; \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->counter_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NEW_COUNTER_A(gptr,h,h_tail,name,domain,type) { \
|
||||
h = (__itt_counter_info_t*)malloc(sizeof(__itt_counter_info_t)); \
|
||||
if (h != NULL) { \
|
||||
h->nameA = name ? __itt_fstrdup(name) : NULL; \
|
||||
h->nameW = NULL; \
|
||||
h->domainA = domain ? __itt_fstrdup(domain) : NULL; \
|
||||
h->domainW = NULL; \
|
||||
h->type = type; \
|
||||
h->index = 0; \
|
||||
h->next = NULL; \
|
||||
if (h_tail == NULL) \
|
||||
(gptr)->counter_list = h; \
|
||||
else \
|
||||
h_tail->next = h; \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* _ITTNOTIFY_CONFIG_H_ */
|
||||
+1236
File diff suppressed because it is too large
Load Diff
+389
@@ -0,0 +1,389 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#include "ittnotify_config.h"
|
||||
|
||||
#ifndef ITT_FORMAT_DEFINED
|
||||
# ifndef ITT_FORMAT
|
||||
# define ITT_FORMAT
|
||||
# endif /* ITT_FORMAT */
|
||||
# ifndef ITT_NO_PARAMS
|
||||
# define ITT_NO_PARAMS
|
||||
# endif /* ITT_NO_PARAMS */
|
||||
#endif /* ITT_FORMAT_DEFINED */
|
||||
|
||||
/*
|
||||
* parameters for macro expected:
|
||||
* ITT_STUB(api, type, func_name, arguments, params, func_name_in_dll, group, printf_fmt)
|
||||
*/
|
||||
#ifdef __ITT_INTERNAL_INIT
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_domain*, domain_createA, (const char *name), (ITT_FORMAT name), domain_createA, __itt_group_structure, "\"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_domain*, domain_createW, (const wchar_t *name), (ITT_FORMAT name), domain_createW, __itt_group_structure, "\"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_domain*, domain_create, (const char *name), (ITT_FORMAT name), domain_create, __itt_group_structure, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_string_handle*, string_handle_createA, (const char *name), (ITT_FORMAT name), string_handle_createA, __itt_group_structure, "\"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_string_handle*, string_handle_createW, (const wchar_t *name), (ITT_FORMAT name), string_handle_createW, __itt_group_structure, "\"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_string_handle*, string_handle_create, (const char *name), (ITT_FORMAT name), string_handle_create, __itt_group_structure, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_createA, (const char *name, const char *domain), (ITT_FORMAT name, domain), counter_createA, __itt_group_counter, "\"%s\", \"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_createW, (const wchar_t *name, const wchar_t *domain), (ITT_FORMAT name, domain), counter_createW, __itt_group_counter, "\"%s\", \"%s\"")
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_create, (const char *name, const char *domain), (ITT_FORMAT name, domain), counter_create, __itt_group_counter, "\"%s\", \"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_create_typedA, (const char *name, const char *domain, __itt_metadata_type type), (ITT_FORMAT name, domain, type), counter_create_typedA, __itt_group_counter, "\"%s\", \"%s\", %d")
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_create_typedW, (const wchar_t *name, const wchar_t *domain, __itt_metadata_type type), (ITT_FORMAT name, domain, type), counter_create_typedW, __itt_group_counter, "\"%s\", \"%s\", %d")
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_counter, counter_create_typed, (const char *name, const char *domain, __itt_metadata_type type), (ITT_FORMAT name, domain, type), counter_create_typed, __itt_group_counter, "\"%s\", \"%s\", %d")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
|
||||
ITT_STUBV(ITTAPI, void, pause, (void), (ITT_NO_PARAMS), pause, __itt_group_control | __itt_group_legacy, "no args")
|
||||
ITT_STUBV(ITTAPI, void, resume, (void), (ITT_NO_PARAMS), resume, __itt_group_control | __itt_group_legacy, "no args")
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, thread_set_nameA, (const char *name), (ITT_FORMAT name), thread_set_nameA, __itt_group_thread, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, thread_set_nameW, (const wchar_t *name), (ITT_FORMAT name), thread_set_nameW, __itt_group_thread, "\"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, thread_set_name, (const char *name), (ITT_FORMAT name), thread_set_name, __itt_group_thread, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, thread_ignore, (void), (ITT_NO_PARAMS), thread_ignore, __itt_group_thread, "no args")
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(LIBITTAPI, int, thr_name_setA, (const char *name, int namelen), (ITT_FORMAT name, namelen), thr_name_setA, __itt_group_thread | __itt_group_legacy, "\"%s\", %d")
|
||||
ITT_STUB(LIBITTAPI, int, thr_name_setW, (const wchar_t *name, int namelen), (ITT_FORMAT name, namelen), thr_name_setW, __itt_group_thread | __itt_group_legacy, "\"%S\", %d")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(LIBITTAPI, int, thr_name_set, (const char *name, int namelen), (ITT_FORMAT name, namelen), thr_name_set, __itt_group_thread | __itt_group_legacy, "\"%s\", %d")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(LIBITTAPI, void, thr_ignore, (void), (ITT_NO_PARAMS), thr_ignore, __itt_group_thread | __itt_group_legacy, "no args")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
ITT_STUBV(ITTAPI, void, enable_attach, (void), (ITT_NO_PARAMS), enable_attach, __itt_group_all, "no args")
|
||||
|
||||
#else /* __ITT_INTERNAL_INIT */
|
||||
|
||||
ITT_STUBV(ITTAPI, void, detach, (void), (ITT_NO_PARAMS), detach, __itt_group_control | __itt_group_legacy, "no args")
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, sync_createA, (void *addr, const char *objtype, const char *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_createA, __itt_group_sync | __itt_group_fsync, "%p, \"%s\", \"%s\", %x")
|
||||
ITT_STUBV(ITTAPI, void, sync_createW, (void *addr, const wchar_t *objtype, const wchar_t *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_createW, __itt_group_sync | __itt_group_fsync, "%p, \"%S\", \"%S\", %x")
|
||||
ITT_STUBV(ITTAPI, void, sync_renameA, (void *addr, const char *name), (ITT_FORMAT addr, name), sync_renameA, __itt_group_sync | __itt_group_fsync, "%p, \"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, sync_renameW, (void *addr, const wchar_t *name), (ITT_FORMAT addr, name), sync_renameW, __itt_group_sync | __itt_group_fsync, "%p, \"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, sync_create, (void *addr, const char *objtype, const char *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_create, __itt_group_sync | __itt_group_fsync, "%p, \"%s\", \"%s\", %x")
|
||||
ITT_STUBV(ITTAPI, void, sync_rename, (void *addr, const char *name), (ITT_FORMAT addr, name), sync_rename, __itt_group_sync | __itt_group_fsync, "%p, \"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, sync_destroy, (void *addr), (ITT_FORMAT addr), sync_destroy, __itt_group_sync | __itt_group_fsync, "%p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, sync_prepare, (void* addr), (ITT_FORMAT addr), sync_prepare, __itt_group_sync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, sync_cancel, (void *addr), (ITT_FORMAT addr), sync_cancel, __itt_group_sync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, sync_acquired, (void *addr), (ITT_FORMAT addr), sync_acquired, __itt_group_sync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, sync_releasing, (void* addr), (ITT_FORMAT addr), sync_releasing, __itt_group_sync, "%p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, suppress_push, (unsigned int mask), (ITT_FORMAT mask), suppress_push, __itt_group_suppress, "%p")
|
||||
ITT_STUBV(ITTAPI, void, suppress_pop, (void), (ITT_NO_PARAMS), suppress_pop, __itt_group_suppress, "no args")
|
||||
ITT_STUBV(ITTAPI, void, suppress_mark_range, (__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size),(ITT_FORMAT mode, mask, address, size), suppress_mark_range, __itt_group_suppress, "%d, %p, %p, %d")
|
||||
ITT_STUBV(ITTAPI, void, suppress_clear_range,(__itt_suppress_mode_t mode, unsigned int mask, void * address, size_t size),(ITT_FORMAT mode, mask, address, size), suppress_clear_range,__itt_group_suppress, "%d, %p, %p, %d")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, fsync_prepare, (void* addr), (ITT_FORMAT addr), sync_prepare, __itt_group_fsync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, fsync_cancel, (void *addr), (ITT_FORMAT addr), sync_cancel, __itt_group_fsync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, fsync_acquired, (void *addr), (ITT_FORMAT addr), sync_acquired, __itt_group_fsync, "%p")
|
||||
ITT_STUBV(ITTAPI, void, fsync_releasing, (void* addr), (ITT_FORMAT addr), sync_releasing, __itt_group_fsync, "%p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, model_site_begin, (__itt_model_site *site, __itt_model_site_instance *instance, const char *name), (ITT_FORMAT site, instance, name), model_site_begin, __itt_group_model, "%p, %p, \"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_site_end, (__itt_model_site *site, __itt_model_site_instance *instance), (ITT_FORMAT site, instance), model_site_end, __itt_group_model, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, model_task_begin, (__itt_model_task *task, __itt_model_task_instance *instance, const char *name), (ITT_FORMAT task, instance, name), model_task_begin, __itt_group_model, "%p, %p, \"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_task_end, (__itt_model_task *task, __itt_model_task_instance *instance), (ITT_FORMAT task, instance), model_task_end, __itt_group_model, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, model_lock_acquire, (void *lock), (ITT_FORMAT lock), model_lock_acquire, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_lock_release, (void *lock), (ITT_FORMAT lock), model_lock_release, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_record_allocation, (void *addr, size_t size), (ITT_FORMAT addr, size), model_record_allocation, __itt_group_model, "%p, %d")
|
||||
ITT_STUBV(ITTAPI, void, model_record_deallocation, (void *addr), (ITT_FORMAT addr), model_record_deallocation, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_induction_uses, (void* addr, size_t size), (ITT_FORMAT addr, size), model_induction_uses, __itt_group_model, "%p, %d")
|
||||
ITT_STUBV(ITTAPI, void, model_reduction_uses, (void* addr, size_t size), (ITT_FORMAT addr, size), model_reduction_uses, __itt_group_model, "%p, %d")
|
||||
ITT_STUBV(ITTAPI, void, model_observe_uses, (void* addr, size_t size), (ITT_FORMAT addr, size), model_observe_uses, __itt_group_model, "%p, %d")
|
||||
ITT_STUBV(ITTAPI, void, model_clear_uses, (void* addr), (ITT_FORMAT addr), model_clear_uses, __itt_group_model, "%p")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, model_site_beginW, (const wchar_t *name), (ITT_FORMAT name), model_site_beginW, __itt_group_model, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_task_beginW, (const wchar_t *name), (ITT_FORMAT name), model_task_beginW, __itt_group_model, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_iteration_taskW, (const wchar_t *name), (ITT_FORMAT name), model_iteration_taskW, __itt_group_model, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, model_site_beginA, (const char *name), (ITT_FORMAT name), model_site_beginA, __itt_group_model, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_site_beginAL, (const char *name, size_t len), (ITT_FORMAT name, len), model_site_beginAL, __itt_group_model, "\"%s\", %d")
|
||||
ITT_STUBV(ITTAPI, void, model_task_beginA, (const char *name), (ITT_FORMAT name), model_task_beginA, __itt_group_model, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_task_beginAL, (const char *name, size_t len), (ITT_FORMAT name, len), model_task_beginAL, __itt_group_model, "\"%s\", %d")
|
||||
ITT_STUBV(ITTAPI, void, model_iteration_taskA, (const char *name), (ITT_FORMAT name), model_iteration_taskA, __itt_group_model, "\"%s\"")
|
||||
ITT_STUBV(ITTAPI, void, model_iteration_taskAL, (const char *name, size_t len), (ITT_FORMAT name, len), model_iteration_taskAL, __itt_group_model, "\"%s\", %d")
|
||||
ITT_STUBV(ITTAPI, void, model_site_end_2, (void), (ITT_NO_PARAMS), model_site_end_2, __itt_group_model, "no args")
|
||||
ITT_STUBV(ITTAPI, void, model_task_end_2, (void), (ITT_NO_PARAMS), model_task_end_2, __itt_group_model, "no args")
|
||||
ITT_STUBV(ITTAPI, void, model_lock_acquire_2, (void *lock), (ITT_FORMAT lock), model_lock_acquire_2, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_lock_release_2, (void *lock), (ITT_FORMAT lock), model_lock_release_2, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_aggregate_task, (size_t count), (ITT_FORMAT count), model_aggregate_task, __itt_group_model, "%d")
|
||||
ITT_STUBV(ITTAPI, void, model_disable_push, (__itt_model_disable x), (ITT_FORMAT x), model_disable_push, __itt_group_model, "%p")
|
||||
ITT_STUBV(ITTAPI, void, model_disable_pop, (void), (ITT_NO_PARAMS), model_disable_pop, __itt_group_model, "no args")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_heap_function, heap_function_createA, (const char *name, const char *domain), (ITT_FORMAT name, domain), heap_function_createA, __itt_group_heap, "\"%s\", \"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_heap_function, heap_function_createW, (const wchar_t *name, const wchar_t *domain), (ITT_FORMAT name, domain), heap_function_createW, __itt_group_heap, "\"%s\", \"%s\"")
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_heap_function, heap_function_create, (const char *name, const char *domain), (ITT_FORMAT name, domain), heap_function_create, __itt_group_heap, "\"%s\", \"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
ITT_STUBV(ITTAPI, void, heap_allocate_begin, (__itt_heap_function h, size_t size, int initialized), (ITT_FORMAT h, size, initialized), heap_allocate_begin, __itt_group_heap, "%p, %lu, %d")
|
||||
ITT_STUBV(ITTAPI, void, heap_allocate_end, (__itt_heap_function h, void** addr, size_t size, int initialized), (ITT_FORMAT h, addr, size, initialized), heap_allocate_end, __itt_group_heap, "%p, %p, %lu, %d")
|
||||
ITT_STUBV(ITTAPI, void, heap_free_begin, (__itt_heap_function h, void* addr), (ITT_FORMAT h, addr), heap_free_begin, __itt_group_heap, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, heap_free_end, (__itt_heap_function h, void* addr), (ITT_FORMAT h, addr), heap_free_end, __itt_group_heap, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, heap_reallocate_begin, (__itt_heap_function h, void* addr, size_t new_size, int initialized), (ITT_FORMAT h, addr, new_size, initialized), heap_reallocate_begin, __itt_group_heap, "%p, %p, %lu, %d")
|
||||
ITT_STUBV(ITTAPI, void, heap_reallocate_end, (__itt_heap_function h, void* addr, void** new_addr, size_t new_size, int initialized), (ITT_FORMAT h, addr, new_addr, new_size, initialized), heap_reallocate_end, __itt_group_heap, "%p, %p, %p, %lu, %d")
|
||||
ITT_STUBV(ITTAPI, void, heap_internal_access_begin, (void), (ITT_NO_PARAMS), heap_internal_access_begin, __itt_group_heap, "no args")
|
||||
ITT_STUBV(ITTAPI, void, heap_internal_access_end, (void), (ITT_NO_PARAMS), heap_internal_access_end, __itt_group_heap, "no args")
|
||||
ITT_STUBV(ITTAPI, void, heap_record_memory_growth_begin, (void), (ITT_NO_PARAMS), heap_record_memory_growth_begin, __itt_group_heap, "no args")
|
||||
ITT_STUBV(ITTAPI, void, heap_record_memory_growth_end, (void), (ITT_NO_PARAMS), heap_record_memory_growth_end, __itt_group_heap, "no args")
|
||||
ITT_STUBV(ITTAPI, void, heap_reset_detection, (unsigned int reset_mask), (ITT_FORMAT reset_mask), heap_reset_detection, __itt_group_heap, "%u")
|
||||
ITT_STUBV(ITTAPI, void, heap_record, (unsigned int record_mask), (ITT_FORMAT record_mask), heap_record, __itt_group_heap, "%u")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, id_create, (const __itt_domain *domain, __itt_id id), (ITT_FORMAT domain, id), id_create, __itt_group_structure, "%p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, id_destroy, (const __itt_domain *domain, __itt_id id), (ITT_FORMAT domain, id), id_destroy, __itt_group_structure, "%p, %lu")
|
||||
|
||||
ITT_STUB(ITTAPI, __itt_timestamp, get_timestamp, (void), (ITT_NO_PARAMS), get_timestamp, __itt_group_structure, "no args")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, region_begin, (const __itt_domain *domain, __itt_id id, __itt_id parent, __itt_string_handle *name), (ITT_FORMAT domain, id, parent, name), region_begin, __itt_group_structure, "%p, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, region_end, (const __itt_domain *domain, __itt_id id), (ITT_FORMAT domain, id), region_end, __itt_group_structure, "%p, %lu")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
ITT_STUBV(ITTAPI, void, frame_begin_v3, (const __itt_domain *domain, __itt_id *id), (ITT_FORMAT domain, id), frame_begin_v3, __itt_group_structure, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, frame_end_v3, (const __itt_domain *domain, __itt_id *id), (ITT_FORMAT domain, id), frame_end_v3, __itt_group_structure, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, frame_submit_v3, (const __itt_domain *domain, __itt_id *id, __itt_timestamp begin, __itt_timestamp end), (ITT_FORMAT domain, id, begin, end), frame_submit_v3, __itt_group_structure, "%p, %p, %lu, %lu")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
ITT_STUBV(ITTAPI, void, task_group, (const __itt_domain *domain, __itt_id id, __itt_id parent, __itt_string_handle *name), (ITT_FORMAT domain, id, parent, name), task_group, __itt_group_structure, "%p, %lu, %lu, %p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, task_begin, (const __itt_domain *domain, __itt_id id, __itt_id parent, __itt_string_handle *name), (ITT_FORMAT domain, id, parent, name), task_begin, __itt_group_structure, "%p, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_begin_fn, (const __itt_domain *domain, __itt_id id, __itt_id parent, void* fn), (ITT_FORMAT domain, id, parent, fn), task_begin_fn, __itt_group_structure, "%p, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_end, (const __itt_domain *domain), (ITT_FORMAT domain), task_end, __itt_group_structure, "%p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, counter_inc_v3, (const __itt_domain *domain, __itt_string_handle *name), (ITT_FORMAT domain, name), counter_inc_v3, __itt_group_structure, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, counter_inc_delta_v3, (const __itt_domain *domain, __itt_string_handle *name, unsigned long long value), (ITT_FORMAT domain, name, value), counter_inc_delta_v3, __itt_group_structure, "%p, %p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, counter_dec_v3, (const __itt_domain *domain, __itt_string_handle *name), (ITT_FORMAT domain, name), counter_dec_v3, __itt_group_structure, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, counter_dec_delta_v3, (const __itt_domain *domain, __itt_string_handle *name, unsigned long long value), (ITT_FORMAT domain, name, value), counter_dec_delta_v3, __itt_group_structure, "%p, %p, %lu")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, marker, (const __itt_domain *domain, __itt_id id, __itt_string_handle *name, __itt_scope scope), (ITT_FORMAT domain, id, name, scope), marker, __itt_group_structure, "%p, %lu, %p, %d")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, metadata_add, (const __itt_domain *domain, __itt_id id, __itt_string_handle *key, __itt_metadata_type type, size_t count, void *data), (ITT_FORMAT domain, id, key, type, count, data), metadata_add, __itt_group_structure, "%p, %lu, %p, %d, %lu, %p")
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_addA, (const __itt_domain *domain, __itt_id id, __itt_string_handle *key, const char* data, size_t length), (ITT_FORMAT domain, id, key, data, length), metadata_str_addA, __itt_group_structure, "%p, %lu, %p, %p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_addW, (const __itt_domain *domain, __itt_id id, __itt_string_handle *key, const wchar_t* data, size_t length), (ITT_FORMAT domain, id, key, data, length), metadata_str_addW, __itt_group_structure, "%p, %lu, %p, %p, %lu")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_add, (const __itt_domain *domain, __itt_id id, __itt_string_handle *key, const char* data, size_t length), (ITT_FORMAT domain, id, key, data, length), metadata_str_add, __itt_group_structure, "%p, %lu, %p, %p, %lu")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
ITT_STUBV(ITTAPI, void, relation_add_to_current, (const __itt_domain *domain, __itt_relation relation, __itt_id tail), (ITT_FORMAT domain, relation, tail), relation_add_to_current, __itt_group_structure, "%p, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, relation_add, (const __itt_domain *domain, __itt_id head, __itt_relation relation, __itt_id tail), (ITT_FORMAT domain, head, relation, tail), relation_add, __itt_group_structure, "%p, %p, %lu, %p")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(LIBITTAPI, __itt_event, event_createA, (const char *name, int namelen), (ITT_FORMAT name, namelen), event_createA, __itt_group_mark | __itt_group_legacy, "\"%s\", %d")
|
||||
ITT_STUB(LIBITTAPI, __itt_event, event_createW, (const wchar_t *name, int namelen), (ITT_FORMAT name, namelen), event_createW, __itt_group_mark | __itt_group_legacy, "\"%S\", %d")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(LIBITTAPI, __itt_event, event_create, (const char *name, int namelen), (ITT_FORMAT name, namelen), event_create, __itt_group_mark | __itt_group_legacy, "\"%s\", %d")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(LIBITTAPI, int, event_start, (__itt_event event), (ITT_FORMAT event), event_start, __itt_group_mark | __itt_group_legacy, "%d")
|
||||
ITT_STUB(LIBITTAPI, int, event_end, (__itt_event event), (ITT_FORMAT event), event_end, __itt_group_mark | __itt_group_legacy, "%d")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, sync_set_nameA, (void *addr, const char *objtype, const char *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_set_nameA, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p, \"%s\", \"%s\", %x")
|
||||
ITT_STUBV(ITTAPI, void, sync_set_nameW, (void *addr, const wchar_t *objtype, const wchar_t *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_set_nameW, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p, \"%S\", \"%S\", %x")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, sync_set_name, (void *addr, const char *objtype, const char *objname, int attribute), (ITT_FORMAT addr, objtype, objname, attribute), sync_set_name, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "p, \"%s\", \"%s\", %x")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(LIBITTAPI, int, notify_sync_nameA, (void *p, const char *objtype, int typelen, const char *objname, int namelen, int attribute), (ITT_FORMAT p, objtype, typelen, objname, namelen, attribute), notify_sync_nameA, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p, \"%s\", %d, \"%s\", %d, %x")
|
||||
ITT_STUB(LIBITTAPI, int, notify_sync_nameW, (void *p, const wchar_t *objtype, int typelen, const wchar_t *objname, int namelen, int attribute), (ITT_FORMAT p, objtype, typelen, objname, namelen, attribute), notify_sync_nameW, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p, \"%S\", %d, \"%S\", %d, %x")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(LIBITTAPI, int, notify_sync_name, (void *p, const char *objtype, int typelen, const char *objname, int namelen, int attribute), (ITT_FORMAT p, objtype, typelen, objname, namelen, attribute), notify_sync_name, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p, \"%s\", %d, \"%s\", %d, %x")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
ITT_STUBV(LIBITTAPI, void, notify_sync_prepare, (void *p), (ITT_FORMAT p), notify_sync_prepare, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p")
|
||||
ITT_STUBV(LIBITTAPI, void, notify_sync_cancel, (void *p), (ITT_FORMAT p), notify_sync_cancel, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p")
|
||||
ITT_STUBV(LIBITTAPI, void, notify_sync_acquired, (void *p), (ITT_FORMAT p), notify_sync_acquired, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p")
|
||||
ITT_STUBV(LIBITTAPI, void, notify_sync_releasing, (void *p), (ITT_FORMAT p), notify_sync_releasing, __itt_group_sync | __itt_group_fsync | __itt_group_legacy, "%p")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
ITT_STUBV(LIBITTAPI, void, memory_read, (void *addr, size_t size), (ITT_FORMAT addr, size), memory_read, __itt_group_legacy, "%p, %lu")
|
||||
ITT_STUBV(LIBITTAPI, void, memory_write, (void *addr, size_t size), (ITT_FORMAT addr, size), memory_write, __itt_group_legacy, "%p, %lu")
|
||||
ITT_STUBV(LIBITTAPI, void, memory_update, (void *addr, size_t size), (ITT_FORMAT addr, size), memory_update, __itt_group_legacy, "%p, %lu")
|
||||
|
||||
ITT_STUB(LIBITTAPI, __itt_state_t, state_get, (void), (ITT_NO_PARAMS), state_get, __itt_group_legacy, "no args")
|
||||
ITT_STUB(LIBITTAPI, __itt_state_t, state_set, (__itt_state_t s), (ITT_FORMAT s), state_set, __itt_group_legacy, "%d")
|
||||
ITT_STUB(LIBITTAPI, __itt_obj_state_t, obj_mode_set, (__itt_obj_prop_t p, __itt_obj_state_t s), (ITT_FORMAT p, s), obj_mode_set, __itt_group_legacy, "%d, %d")
|
||||
ITT_STUB(LIBITTAPI, __itt_thr_state_t, thr_mode_set, (__itt_thr_prop_t p, __itt_thr_state_t s), (ITT_FORMAT p, s), thr_mode_set, __itt_group_legacy, "%d, %d")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_frame, frame_createA, (const char *domain), (ITT_FORMAT domain), frame_createA, __itt_group_frame, "\"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_frame, frame_createW, (const wchar_t *domain), (ITT_FORMAT domain), frame_createW, __itt_group_frame, "\"%s\"")
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_frame, frame_create, (const char *domain), (ITT_FORMAT domain), frame_create, __itt_group_frame, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
ITT_STUBV(ITTAPI, void, frame_begin, (__itt_frame frame), (ITT_FORMAT frame), frame_begin, __itt_group_frame, "%p")
|
||||
ITT_STUBV(ITTAPI, void, frame_end, (__itt_frame frame), (ITT_FORMAT frame), frame_end, __itt_group_frame, "%p")
|
||||
|
||||
ITT_STUBV(ITTAPI, void, counter_destroy, (__itt_counter id), (ITT_FORMAT id), counter_destroy, __itt_group_counter, "%p")
|
||||
ITT_STUBV(ITTAPI, void, counter_inc, (__itt_counter id), (ITT_FORMAT id), counter_inc, __itt_group_counter, "%p")
|
||||
ITT_STUBV(ITTAPI, void, counter_inc_delta, (__itt_counter id, unsigned long long value), (ITT_FORMAT id, value), counter_inc_delta, __itt_group_counter, "%p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, counter_dec, (__itt_counter id), (ITT_FORMAT id), counter_dec, __itt_group_counter, "%p")
|
||||
ITT_STUBV(ITTAPI, void, counter_dec_delta, (__itt_counter id, unsigned long long value), (ITT_FORMAT id, value), counter_dec_delta, __itt_group_counter, "%p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, counter_set_value, (__itt_counter id, void *value_ptr), (ITT_FORMAT id, value_ptr), counter_set_value, __itt_group_counter, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, counter_set_value_ex, (__itt_counter id, __itt_clock_domain *clock_domain, unsigned long long timestamp, void *value_ptr), (ITT_FORMAT id, clock_domain, timestamp, value_ptr), counter_set_value_ex, __itt_group_counter, "%p, %p, %llu, %p")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, __itt_mark_type, mark_createA, (const char *name), (ITT_FORMAT name), mark_createA, __itt_group_mark, "\"%s\"")
|
||||
ITT_STUB(ITTAPI, __itt_mark_type, mark_createW, (const wchar_t *name), (ITT_FORMAT name), mark_createW, __itt_group_mark, "\"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, __itt_mark_type, mark_create, (const char *name), (ITT_FORMAT name), mark_create, __itt_group_mark, "\"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, int, markA, (__itt_mark_type mt, const char *parameter), (ITT_FORMAT mt, parameter), markA, __itt_group_mark, "%d, \"%s\"")
|
||||
ITT_STUB(ITTAPI, int, markW, (__itt_mark_type mt, const wchar_t *parameter), (ITT_FORMAT mt, parameter), markW, __itt_group_mark, "%d, \"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, int, mark, (__itt_mark_type mt, const char *parameter), (ITT_FORMAT mt, parameter), mark, __itt_group_mark, "%d, \"%s\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, int, mark_off, (__itt_mark_type mt), (ITT_FORMAT mt), mark_off, __itt_group_mark, "%d")
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, int, mark_globalA, (__itt_mark_type mt, const char *parameter), (ITT_FORMAT mt, parameter), mark_globalA, __itt_group_mark, "%d, \"%s\"")
|
||||
ITT_STUB(ITTAPI, int, mark_globalW, (__itt_mark_type mt, const wchar_t *parameter), (ITT_FORMAT mt, parameter), mark_globalW, __itt_group_mark, "%d, \"%S\"")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, int, mark_global, (__itt_mark_type mt, const char *parameter), (ITT_FORMAT mt, parameter), mark_global, __itt_group_mark, "%d, \"%S\"")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, int, mark_global_off, (__itt_mark_type mt), (ITT_FORMAT mt), mark_global_off, __itt_group_mark, "%d")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
ITT_STUB(ITTAPI, __itt_caller, stack_caller_create, (void), (ITT_NO_PARAMS), stack_caller_create, __itt_group_stitch, "no args")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
ITT_STUBV(ITTAPI, void, stack_caller_destroy, (__itt_caller id), (ITT_FORMAT id), stack_caller_destroy, __itt_group_stitch, "%p")
|
||||
ITT_STUBV(ITTAPI, void, stack_callee_enter, (__itt_caller id), (ITT_FORMAT id), stack_callee_enter, __itt_group_stitch, "%p")
|
||||
ITT_STUBV(ITTAPI, void, stack_callee_leave, (__itt_caller id), (ITT_FORMAT id), stack_callee_leave, __itt_group_stitch, "%p")
|
||||
|
||||
ITT_STUB(ITTAPI, __itt_clock_domain*, clock_domain_create, (__itt_get_clock_info_fn fn, void* fn_data), (ITT_FORMAT fn, fn_data), clock_domain_create, __itt_group_structure, "%p, %p")
|
||||
ITT_STUBV(ITTAPI, void, clock_domain_reset, (void), (ITT_NO_PARAMS), clock_domain_reset, __itt_group_structure, "no args")
|
||||
ITT_STUBV(ITTAPI, void, id_create_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id), (ITT_FORMAT domain, clock_domain, timestamp, id), id_create_ex, __itt_group_structure, "%p, %p, %lu, %lu")
|
||||
ITT_STUBV(ITTAPI, void, id_destroy_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id), (ITT_FORMAT domain, clock_domain, timestamp, id), id_destroy_ex, __itt_group_structure, "%p, %p, %lu, %lu")
|
||||
ITT_STUBV(ITTAPI, void, task_begin_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id, __itt_id parentid, __itt_string_handle *name), (ITT_FORMAT domain, clock_domain, timestamp, id, parentid, name), task_begin_ex, __itt_group_structure, "%p, %p, %lu, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_begin_fn_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id, __itt_id parentid, void* fn), (ITT_FORMAT domain, clock_domain, timestamp, id, parentid, fn), task_begin_fn_ex, __itt_group_structure, "%p, %p, %lu, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_end_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp), (ITT_FORMAT domain, clock_domain, timestamp), task_end_ex, __itt_group_structure, "%p, %p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, task_begin_overlapped, (const __itt_domain *domain, __itt_id id, __itt_id parent, __itt_string_handle *name), (ITT_FORMAT domain, id, parent, name), task_begin_overlapped, __itt_group_structure, "%p, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_begin_overlapped_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id, __itt_id parentid, __itt_string_handle *name), (ITT_FORMAT domain, clock_domain, timestamp, id, parentid, name), task_begin_overlapped_ex, __itt_group_structure, "%p, %p, %lu, %lu, %lu, %p")
|
||||
ITT_STUBV(ITTAPI, void, task_end_overlapped, (const __itt_domain *domain, __itt_id id), (ITT_FORMAT domain, id), task_end_overlapped, __itt_group_structure, "%p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, task_end_overlapped_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id), (ITT_FORMAT domain, clock_domain, timestamp, id), task_end_overlapped_ex, __itt_group_structure, "%p, %p, %lu, %lu")
|
||||
ITT_STUBV(ITTAPI, void, marker_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id id, __itt_string_handle *name, __itt_scope scope), (ITT_FORMAT domain, clock_domain, timestamp, id, name, scope), marker_ex, __itt_group_structure, "%p, %p, %lu, %lu, %p, %d")
|
||||
ITT_STUBV(ITTAPI, void, metadata_add_with_scope, (const __itt_domain *domain, __itt_scope scope, __itt_string_handle *key, __itt_metadata_type type, size_t count, void *data), (ITT_FORMAT domain, scope, key, type, count, data), metadata_add_with_scope, __itt_group_structure, "%p, %d, %p, %d, %lu, %p")
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_add_with_scopeA, (const __itt_domain *domain, __itt_scope scope, __itt_string_handle *key, const char *data, size_t length), (ITT_FORMAT domain, scope, key, data, length), metadata_str_add_with_scopeA, __itt_group_structure, "%p, %d, %p, %p, %lu")
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_add_with_scopeW, (const __itt_domain *domain, __itt_scope scope, __itt_string_handle *key, const wchar_t *data, size_t length), (ITT_FORMAT domain, scope, key, data, length), metadata_str_add_with_scopeW, __itt_group_structure, "%p, %d, %p, %p, %lu")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, metadata_str_add_with_scope, (const __itt_domain *domain, __itt_scope scope, __itt_string_handle *key, const char *data, size_t length), (ITT_FORMAT domain, scope, key, data, length), metadata_str_add_with_scope, __itt_group_structure, "%p, %d, %p, %p, %lu")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, relation_add_to_current_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_relation relation, __itt_id tail), (ITT_FORMAT domain, clock_domain, timestamp, relation, tail), relation_add_to_current_ex, __itt_group_structure, "%p, %p, %lu, %d, %lu")
|
||||
ITT_STUBV(ITTAPI, void, relation_add_ex, (const __itt_domain *domain, __itt_clock_domain* clock_domain, unsigned long long timestamp, __itt_id head, __itt_relation relation, __itt_id tail), (ITT_FORMAT domain, clock_domain, timestamp, head, relation, tail), relation_add_ex, __itt_group_structure, "%p, %p, %lu, %lu, %d, %lu")
|
||||
ITT_STUB(ITTAPI, __itt_track_group*, track_group_create, (__itt_string_handle* name, __itt_track_group_type track_group_type), (ITT_FORMAT name, track_group_type), track_group_create, __itt_group_structure, "%p, %d")
|
||||
ITT_STUB(ITTAPI, __itt_track*, track_create, (__itt_track_group* track_group,__itt_string_handle* name, __itt_track_type track_type), (ITT_FORMAT track_group, name, track_type), track_create, __itt_group_structure, "%p, %p, %d")
|
||||
ITT_STUBV(ITTAPI, void, set_track, (__itt_track *track), (ITT_FORMAT track), set_track, __itt_group_structure, "%p")
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
ITT_STUB(ITTAPI, const char*, api_version, (void), (ITT_NO_PARAMS), api_version, __itt_group_all & ~__itt_group_legacy, "no args")
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUB(ITTAPI, int, av_saveA, (void *data, int rank, const int *dimensions, int type, const char *filePath, int columnOrder), (ITT_FORMAT data, rank, dimensions, type, filePath, columnOrder), av_saveA, __itt_group_arrays, "%p, %d, %p, %d, \"%s\", %d")
|
||||
ITT_STUB(ITTAPI, int, av_saveW, (void *data, int rank, const int *dimensions, int type, const wchar_t *filePath, int columnOrder), (ITT_FORMAT data, rank, dimensions, type, filePath, columnOrder), av_saveW, __itt_group_arrays, "%p, %d, %p, %d, \"%S\", %d")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUB(ITTAPI, int, av_save, (void *data, int rank, const int *dimensions, int type, const char *filePath, int columnOrder), (ITT_FORMAT data, rank, dimensions, type, filePath, columnOrder), av_save, __itt_group_arrays, "%p, %d, %p, %d, \"%s\", %d")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
#ifndef __ITT_INTERNAL_BODY
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
ITT_STUBV(ITTAPI, void, module_loadA, (void *start_addr, void* end_addr, const char *path), (ITT_FORMAT start_addr, end_addr, path), module_loadA, __itt_group_none, "%p, %p, %p")
|
||||
ITT_STUBV(ITTAPI, void, module_loadW, (void *start_addr, void* end_addr, const wchar_t *path), (ITT_FORMAT start_addr, end_addr, path), module_loadW, __itt_group_none, "%p, %p, %p")
|
||||
#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
|
||||
ITT_STUBV(ITTAPI, void, module_load, (void *start_addr, void *end_addr, const char *path), (ITT_FORMAT start_addr, end_addr, path), module_load, __itt_group_none, "%p, %p, %p")
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#endif /* __ITT_INTERNAL_BODY */
|
||||
|
||||
|
||||
#endif /* __ITT_INTERNAL_INIT */
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#ifndef _ITTNOTIFY_TYPES_H_
|
||||
#define _ITTNOTIFY_TYPES_H_
|
||||
|
||||
typedef enum ___itt_group_id
|
||||
{
|
||||
__itt_group_none = 0,
|
||||
__itt_group_legacy = 1<<0,
|
||||
__itt_group_control = 1<<1,
|
||||
__itt_group_thread = 1<<2,
|
||||
__itt_group_mark = 1<<3,
|
||||
__itt_group_sync = 1<<4,
|
||||
__itt_group_fsync = 1<<5,
|
||||
__itt_group_jit = 1<<6,
|
||||
__itt_group_model = 1<<7,
|
||||
__itt_group_splitter_min = 1<<7,
|
||||
__itt_group_counter = 1<<8,
|
||||
__itt_group_frame = 1<<9,
|
||||
__itt_group_stitch = 1<<10,
|
||||
__itt_group_heap = 1<<11,
|
||||
__itt_group_splitter_max = 1<<12,
|
||||
__itt_group_structure = 1<<12,
|
||||
__itt_group_suppress = 1<<13,
|
||||
__itt_group_arrays = 1<<14,
|
||||
__itt_group_all = -1
|
||||
} __itt_group_id;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
typedef struct ___itt_group_list
|
||||
{
|
||||
__itt_group_id id;
|
||||
const char* name;
|
||||
} __itt_group_list;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define ITT_GROUP_LIST(varname) \
|
||||
static __itt_group_list varname[] = { \
|
||||
{ __itt_group_all, "all" }, \
|
||||
{ __itt_group_control, "control" }, \
|
||||
{ __itt_group_thread, "thread" }, \
|
||||
{ __itt_group_mark, "mark" }, \
|
||||
{ __itt_group_sync, "sync" }, \
|
||||
{ __itt_group_fsync, "fsync" }, \
|
||||
{ __itt_group_jit, "jit" }, \
|
||||
{ __itt_group_model, "model" }, \
|
||||
{ __itt_group_counter, "counter" }, \
|
||||
{ __itt_group_frame, "frame" }, \
|
||||
{ __itt_group_stitch, "stitch" }, \
|
||||
{ __itt_group_heap, "heap" }, \
|
||||
{ __itt_group_structure, "structure" }, \
|
||||
{ __itt_group_suppress, "suppress" }, \
|
||||
{ __itt_group_arrays, "arrays" }, \
|
||||
{ __itt_group_none, NULL } \
|
||||
}
|
||||
|
||||
#endif /* _ITTNOTIFY_TYPES_H_ */
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
/* <copyright>
|
||||
This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
redistributing this file, you may do so under either license.
|
||||
|
||||
GPL LICENSE SUMMARY
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
The full GNU General Public License is included in this distribution
|
||||
in the file called LICENSE.GPL.
|
||||
|
||||
Contact Information:
|
||||
http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) 2005-2014 Intel Corporation. All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Intel Corporation nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright> */
|
||||
|
||||
#include "ittnotify_config.h"
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#include <windows.h>
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
#if ITT_PLATFORM != ITT_PLATFORM_MAC && ITT_PLATFORM != ITT_PLATFORM_FREEBSD
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jitprofiling.h"
|
||||
|
||||
static const char rcsid[] = "\n@(#) $Revision: 471937 $\n";
|
||||
|
||||
#define DLL_ENVIRONMENT_VAR "VS_PROFILER"
|
||||
|
||||
#ifndef NEW_DLL_ENVIRONMENT_VAR
|
||||
#if ITT_ARCH==ITT_ARCH_IA32
|
||||
#define NEW_DLL_ENVIRONMENT_VAR "INTEL_JIT_PROFILER32"
|
||||
#else
|
||||
#define NEW_DLL_ENVIRONMENT_VAR "INTEL_JIT_PROFILER64"
|
||||
#endif
|
||||
#endif /* NEW_DLL_ENVIRONMENT_VAR */
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#define DEFAULT_DLLNAME "JitPI.dll"
|
||||
HINSTANCE m_libHandle = NULL;
|
||||
#elif ITT_PLATFORM==ITT_PLATFORM_MAC
|
||||
#define DEFAULT_DLLNAME "libJitPI.dylib"
|
||||
void* m_libHandle = NULL;
|
||||
#else
|
||||
#define DEFAULT_DLLNAME "libJitPI.so"
|
||||
void* m_libHandle = NULL;
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
/* default location of JIT profiling agent on Android */
|
||||
#define ANDROID_JIT_AGENT_PATH "/data/intel/libittnotify.so"
|
||||
|
||||
/* the function pointers */
|
||||
typedef unsigned int(JITAPI *TPInitialize)(void);
|
||||
static TPInitialize FUNC_Initialize=NULL;
|
||||
|
||||
typedef unsigned int(JITAPI *TPNotify)(unsigned int, void*);
|
||||
static TPNotify FUNC_NotifyEvent=NULL;
|
||||
|
||||
static iJIT_IsProfilingActiveFlags executionMode = iJIT_NOTHING_RUNNING;
|
||||
|
||||
/* end collector dll part. */
|
||||
|
||||
/* loadiJIT_Funcs() : this function is called just in the beginning
|
||||
* and is responsible to load the functions from BistroJavaCollector.dll
|
||||
* result:
|
||||
* on success: the functions loads, iJIT_DLL_is_missing=0, return value = 1
|
||||
* on failure: the functions are NULL, iJIT_DLL_is_missing=1, return value = 0
|
||||
*/
|
||||
static int loadiJIT_Funcs(void);
|
||||
|
||||
/* global representing whether the collector can't be loaded */
|
||||
static int iJIT_DLL_is_missing = 0;
|
||||
|
||||
ITT_EXTERN_C int JITAPI
|
||||
iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData)
|
||||
{
|
||||
int ReturnValue = 0;
|
||||
|
||||
/* initialization part - the collector has not been loaded yet. */
|
||||
if (!FUNC_NotifyEvent)
|
||||
{
|
||||
if (iJIT_DLL_is_missing)
|
||||
return 0;
|
||||
|
||||
if (!loadiJIT_Funcs())
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (event_type == iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED ||
|
||||
event_type == iJVM_EVENT_TYPE_METHOD_UPDATE)
|
||||
{
|
||||
if (((piJIT_Method_Load)EventSpecificData)->method_id == 0)
|
||||
return 0;
|
||||
}
|
||||
else if (event_type == iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2)
|
||||
{
|
||||
if (((piJIT_Method_Load_V2)EventSpecificData)->method_id == 0)
|
||||
return 0;
|
||||
}
|
||||
else if (event_type == iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V3)
|
||||
{
|
||||
if (((piJIT_Method_Load_V3)EventSpecificData)->method_id == 0)
|
||||
return 0;
|
||||
}
|
||||
else if (event_type == iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED)
|
||||
{
|
||||
if (((piJIT_Method_Inline_Load)EventSpecificData)->method_id == 0 ||
|
||||
((piJIT_Method_Inline_Load)EventSpecificData)->parent_method_id == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReturnValue = (int)FUNC_NotifyEvent(event_type, EventSpecificData);
|
||||
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
ITT_EXTERN_C iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive()
|
||||
{
|
||||
if (!iJIT_DLL_is_missing)
|
||||
{
|
||||
loadiJIT_Funcs();
|
||||
}
|
||||
|
||||
return executionMode;
|
||||
}
|
||||
|
||||
/* This function loads the collector dll and the relevant functions.
|
||||
* on success: all functions load, iJIT_DLL_is_missing = 0, return value = 1
|
||||
* on failure: all functions are NULL, iJIT_DLL_is_missing = 1, return value = 0
|
||||
*/
|
||||
static int loadiJIT_Funcs()
|
||||
{
|
||||
static int bDllWasLoaded = 0;
|
||||
char *dllName = (char*)rcsid; /* !! Just to avoid unused code elimination */
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
DWORD dNameLength = 0;
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
if(bDllWasLoaded)
|
||||
{
|
||||
/* dll was already loaded, no need to do it for the second time */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Assumes that the DLL will not be found */
|
||||
iJIT_DLL_is_missing = 1;
|
||||
FUNC_NotifyEvent = NULL;
|
||||
|
||||
if (m_libHandle)
|
||||
{
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
FreeLibrary(m_libHandle);
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
dlclose(m_libHandle);
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
m_libHandle = NULL;
|
||||
}
|
||||
|
||||
/* Try to get the dll name from the environment */
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
dNameLength = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR, NULL, 0);
|
||||
if (dNameLength)
|
||||
{
|
||||
DWORD envret = 0;
|
||||
dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
|
||||
if(dllName != NULL)
|
||||
{
|
||||
envret = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR,
|
||||
dllName, dNameLength);
|
||||
if (envret)
|
||||
{
|
||||
/* Try to load the dll from the PATH... */
|
||||
m_libHandle = LoadLibraryExA(dllName,
|
||||
NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
}
|
||||
free(dllName);
|
||||
}
|
||||
} else {
|
||||
/* Try to use old VS_PROFILER variable */
|
||||
dNameLength = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR, NULL, 0);
|
||||
if (dNameLength)
|
||||
{
|
||||
DWORD envret = 0;
|
||||
dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
|
||||
if(dllName != NULL)
|
||||
{
|
||||
envret = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR,
|
||||
dllName, dNameLength);
|
||||
if (envret)
|
||||
{
|
||||
/* Try to load the dll from the PATH... */
|
||||
m_libHandle = LoadLibraryA(dllName);
|
||||
}
|
||||
free(dllName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
dllName = getenv(NEW_DLL_ENVIRONMENT_VAR);
|
||||
if (!dllName)
|
||||
dllName = getenv(DLL_ENVIRONMENT_VAR);
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
if (!dllName)
|
||||
dllName = ANDROID_JIT_AGENT_PATH;
|
||||
#endif
|
||||
if (dllName)
|
||||
{
|
||||
/* Try to load the dll from the PATH... */
|
||||
m_libHandle = dlopen(dllName, RTLD_LAZY);
|
||||
}
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
|
||||
if (!m_libHandle)
|
||||
{
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
m_libHandle = LoadLibraryA(DEFAULT_DLLNAME);
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
m_libHandle = dlopen(DEFAULT_DLLNAME, RTLD_LAZY);
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
}
|
||||
|
||||
/* if the dll wasn't loaded - exit. */
|
||||
if (!m_libHandle)
|
||||
{
|
||||
iJIT_DLL_is_missing = 1; /* don't try to initialize
|
||||
* JIT agent the second time
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
FUNC_NotifyEvent = (TPNotify)GetProcAddress(m_libHandle, "NotifyEvent");
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
FUNC_NotifyEvent = (TPNotify)dlsym(m_libHandle, "NotifyEvent");
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
if (!FUNC_NotifyEvent)
|
||||
{
|
||||
FUNC_Initialize = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
FUNC_Initialize = (TPInitialize)GetProcAddress(m_libHandle, "Initialize");
|
||||
#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
FUNC_Initialize = (TPInitialize)dlsym(m_libHandle, "Initialize");
|
||||
#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
|
||||
if (!FUNC_Initialize)
|
||||
{
|
||||
FUNC_NotifyEvent = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
executionMode = (iJIT_IsProfilingActiveFlags)FUNC_Initialize();
|
||||
|
||||
bDllWasLoaded = 1;
|
||||
iJIT_DLL_is_missing = 0; /* DLL is ok. */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ITT_EXTERN_C unsigned int JITAPI iJIT_GetNewMethodID()
|
||||
{
|
||||
static unsigned int methodID = 1;
|
||||
|
||||
if (methodID == 0)
|
||||
return 0; /* ERROR : this is not a valid value */
|
||||
|
||||
return methodID++;
|
||||
}
|
||||
Vendored
-33
@@ -1,33 +0,0 @@
|
||||
Jinja is written and maintained by the Jinja Team and various
|
||||
contributors:
|
||||
|
||||
Lead Developer:
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
||||
|
||||
Developers:
|
||||
|
||||
- Christoph Hack
|
||||
- Georg Brandl
|
||||
|
||||
Contributors:
|
||||
|
||||
- Bryan McLemore
|
||||
- Mickaël Guérin <kael@crocobox.org>
|
||||
- Cameron Knight
|
||||
- Lawrence Journal-World.
|
||||
- David Cramer
|
||||
|
||||
Patches and suggestions:
|
||||
|
||||
- Ronny Pfannschmidt
|
||||
- Axel Böhm
|
||||
- Alexey Melchakov
|
||||
- Bryan McLemore
|
||||
- Clovis Fabricio (nosklo)
|
||||
- Cameron Knight
|
||||
- Peter van Dijk (Habbie)
|
||||
- Stefan Ebner
|
||||
- Rene Leonhardt
|
||||
- Thomas Waldmann
|
||||
- Cory Benfield (Lukasa)
|
||||
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Vendored
-69
@@ -1,69 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2
|
||||
~~~~~~
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
Django inspired non-XML syntax but supports inline expressions and
|
||||
an optional sandboxed environment.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja2 template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__version__ = '2.7.1'
|
||||
|
||||
# high level interface
|
||||
from jinja2.environment import Environment, Template
|
||||
|
||||
# loaders
|
||||
from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
|
||||
DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
|
||||
ModuleLoader
|
||||
|
||||
# bytecode caches
|
||||
from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
|
||||
MemcachedBytecodeCache
|
||||
|
||||
# undefined types
|
||||
from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
|
||||
|
||||
# exceptions
|
||||
from jinja2.exceptions import TemplateError, UndefinedError, \
|
||||
TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
|
||||
TemplateAssertionError
|
||||
|
||||
# decorators and public utilities
|
||||
from jinja2.filters import environmentfilter, contextfilter, \
|
||||
evalcontextfilter
|
||||
from jinja2.utils import Markup, escape, clear_caches, \
|
||||
environmentfunction, evalcontextfunction, contextfunction, \
|
||||
is_undefined
|
||||
|
||||
__all__ = [
|
||||
'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
|
||||
'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
|
||||
'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache',
|
||||
'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
|
||||
'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
|
||||
'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
|
||||
'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
|
||||
'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
|
||||
'evalcontextfilter', 'evalcontextfunction'
|
||||
]
|
||||
Vendored
-150
@@ -1,150 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2._compat
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Some py2/py3 compatibility support based on a stripped down
|
||||
version of six so we don't have to depend on a specific version
|
||||
of it.
|
||||
|
||||
:copyright: Copyright 2013 by the Jinja team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PYPY = hasattr(sys, 'pypy_translation_info')
|
||||
_identity = lambda x: x
|
||||
|
||||
|
||||
if not PY2:
|
||||
unichr = chr
|
||||
range_type = range
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
|
||||
iterkeys = lambda d: iter(d.keys())
|
||||
itervalues = lambda d: iter(d.values())
|
||||
iteritems = lambda d: iter(d.items())
|
||||
|
||||
import pickle
|
||||
from io import BytesIO, StringIO
|
||||
NativeStringIO = StringIO
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
ifilter = filter
|
||||
imap = map
|
||||
izip = zip
|
||||
intern = sys.intern
|
||||
|
||||
implements_iterator = _identity
|
||||
implements_to_string = _identity
|
||||
encode_filename = _identity
|
||||
get_next = lambda x: x.__next__
|
||||
|
||||
else:
|
||||
unichr = unichr
|
||||
text_type = unicode
|
||||
range_type = xrange
|
||||
string_types = (str, unicode)
|
||||
|
||||
iterkeys = lambda d: d.iterkeys()
|
||||
itervalues = lambda d: d.itervalues()
|
||||
iteritems = lambda d: d.iteritems()
|
||||
|
||||
import cPickle as pickle
|
||||
from cStringIO import StringIO as BytesIO, StringIO
|
||||
NativeStringIO = BytesIO
|
||||
|
||||
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||
|
||||
from itertools import imap, izip, ifilter
|
||||
intern = intern
|
||||
|
||||
def implements_iterator(cls):
|
||||
cls.next = cls.__next__
|
||||
del cls.__next__
|
||||
return cls
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||
return cls
|
||||
|
||||
get_next = lambda x: x.next
|
||||
|
||||
def encode_filename(filename):
|
||||
if isinstance(filename, unicode):
|
||||
return filename.encode('utf-8')
|
||||
return filename
|
||||
|
||||
try:
|
||||
next = next
|
||||
except NameError:
|
||||
def next(it):
|
||||
return it.next()
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
# This requires a bit of explanation: the basic idea is to make a
|
||||
# dummy metaclass for one level of class instanciation that replaces
|
||||
# itself with the actual metaclass. Because of internal type checks
|
||||
# we also need to make sure that we downgrade the custom metaclass
|
||||
# for one level to something closer to type (that's why __call__ and
|
||||
# __init__ comes back from type etc.).
|
||||
#
|
||||
# This has the advantage over six.with_metaclass in that it does not
|
||||
# introduce dummy classes into the final MRO.
|
||||
class metaclass(meta):
|
||||
__call__ = type.__call__
|
||||
__init__ = type.__init__
|
||||
def __new__(cls, name, this_bases, d):
|
||||
if this_bases is None:
|
||||
return type.__new__(cls, name, (), d)
|
||||
return meta(name, bases, d)
|
||||
return metaclass('temporary_class', None, {})
|
||||
|
||||
|
||||
try:
|
||||
from collections import Mapping as mapping_types
|
||||
except ImportError:
|
||||
import UserDict
|
||||
mapping_types = (UserDict.UserDict, UserDict.DictMixin, dict)
|
||||
|
||||
|
||||
# common types. These do exist in the special types module too which however
|
||||
# does not exist in IronPython out of the box. Also that way we don't have
|
||||
# to deal with implementation specific stuff here
|
||||
class _C(object):
|
||||
def method(self): pass
|
||||
def _func():
|
||||
yield None
|
||||
function_type = type(_func)
|
||||
generator_type = type(_func())
|
||||
method_type = type(_C().method)
|
||||
code_type = type(_C.method.__code__)
|
||||
try:
|
||||
raise TypeError()
|
||||
except TypeError:
|
||||
_tb = sys.exc_info()[2]
|
||||
traceback_type = type(_tb)
|
||||
frame_type = type(_tb.tb_frame)
|
||||
|
||||
|
||||
try:
|
||||
from urllib.parse import quote_from_bytes as url_quote
|
||||
except ImportError:
|
||||
from urllib import quote as url_quote
|
||||
|
||||
|
||||
try:
|
||||
from thread import allocate_lock
|
||||
except ImportError:
|
||||
try:
|
||||
from threading import Lock as allocate_lock
|
||||
except ImportError:
|
||||
from dummy_thread import allocate_lock
|
||||
Vendored
-132
File diff suppressed because one or more lines are too long
Vendored
-311
@@ -1,311 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.bccache
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module implements the bytecode cache system Jinja is optionally
|
||||
using. This is useful if you have very complex template situations and
|
||||
the compiliation of all those templates slow down your application too
|
||||
much.
|
||||
|
||||
Situations where this is useful are often forking web applications that
|
||||
are initialized on the first request.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from os import path, listdir
|
||||
import sys
|
||||
import marshal
|
||||
import tempfile
|
||||
import fnmatch
|
||||
from hashlib import sha1
|
||||
from jinja2.utils import open_if_exists
|
||||
from jinja2._compat import BytesIO, pickle, PY2, text_type
|
||||
|
||||
|
||||
# marshal works better on 3.x, one hack less required
|
||||
if not PY2:
|
||||
marshal_dump = marshal.dump
|
||||
marshal_load = marshal.load
|
||||
else:
|
||||
|
||||
def marshal_dump(code, f):
|
||||
if isinstance(f, file):
|
||||
marshal.dump(code, f)
|
||||
else:
|
||||
f.write(marshal.dumps(code))
|
||||
|
||||
def marshal_load(f):
|
||||
if isinstance(f, file):
|
||||
return marshal.load(f)
|
||||
return marshal.loads(f.read())
|
||||
|
||||
|
||||
bc_version = 2
|
||||
|
||||
# magic version used to only change with new jinja versions. With 2.6
|
||||
# we change this to also take Python version changes into account. The
|
||||
# reason for this is that Python tends to segfault if fed earlier bytecode
|
||||
# versions because someone thought it would be a good idea to reuse opcodes
|
||||
# or make Python incompatible with earlier versions.
|
||||
bc_magic = 'j2'.encode('ascii') + \
|
||||
pickle.dumps(bc_version, 2) + \
|
||||
pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
|
||||
|
||||
|
||||
class Bucket(object):
|
||||
"""Buckets are used to store the bytecode for one template. It's created
|
||||
and initialized by the bytecode cache and passed to the loading functions.
|
||||
|
||||
The buckets get an internal checksum from the cache assigned and use this
|
||||
to automatically reject outdated cache material. Individual bytecode
|
||||
cache subclasses don't have to care about cache invalidation.
|
||||
"""
|
||||
|
||||
def __init__(self, environment, key, checksum):
|
||||
self.environment = environment
|
||||
self.key = key
|
||||
self.checksum = checksum
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Resets the bucket (unloads the bytecode)."""
|
||||
self.code = None
|
||||
|
||||
def load_bytecode(self, f):
|
||||
"""Loads bytecode from a file or file like object."""
|
||||
# make sure the magic header is correct
|
||||
magic = f.read(len(bc_magic))
|
||||
if magic != bc_magic:
|
||||
self.reset()
|
||||
return
|
||||
# the source code of the file changed, we need to reload
|
||||
checksum = pickle.load(f)
|
||||
if self.checksum != checksum:
|
||||
self.reset()
|
||||
return
|
||||
self.code = marshal_load(f)
|
||||
|
||||
def write_bytecode(self, f):
|
||||
"""Dump the bytecode into the file or file like object passed."""
|
||||
if self.code is None:
|
||||
raise TypeError('can\'t write empty bucket')
|
||||
f.write(bc_magic)
|
||||
pickle.dump(self.checksum, f, 2)
|
||||
marshal_dump(self.code, f)
|
||||
|
||||
def bytecode_from_string(self, string):
|
||||
"""Load bytecode from a string."""
|
||||
self.load_bytecode(BytesIO(string))
|
||||
|
||||
def bytecode_to_string(self):
|
||||
"""Return the bytecode as string."""
|
||||
out = BytesIO()
|
||||
self.write_bytecode(out)
|
||||
return out.getvalue()
|
||||
|
||||
|
||||
class BytecodeCache(object):
|
||||
"""To implement your own bytecode cache you have to subclass this class
|
||||
and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
|
||||
these methods are passed a :class:`~jinja2.bccache.Bucket`.
|
||||
|
||||
A very basic bytecode cache that saves the bytecode on the file system::
|
||||
|
||||
from os import path
|
||||
|
||||
class MyCache(BytecodeCache):
|
||||
|
||||
def __init__(self, directory):
|
||||
self.directory = directory
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
filename = path.join(self.directory, bucket.key)
|
||||
if path.exists(filename):
|
||||
with open(filename, 'rb') as f:
|
||||
bucket.load_bytecode(f)
|
||||
|
||||
def dump_bytecode(self, bucket):
|
||||
filename = path.join(self.directory, bucket.key)
|
||||
with open(filename, 'wb') as f:
|
||||
bucket.write_bytecode(f)
|
||||
|
||||
A more advanced version of a filesystem based bytecode cache is part of
|
||||
Jinja2.
|
||||
"""
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
"""Subclasses have to override this method to load bytecode into a
|
||||
bucket. If they are not able to find code in the cache for the
|
||||
bucket, it must not do anything.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def dump_bytecode(self, bucket):
|
||||
"""Subclasses have to override this method to write the bytecode
|
||||
from a bucket back to the cache. If it unable to do so it must not
|
||||
fail silently but raise an exception.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def clear(self):
|
||||
"""Clears the cache. This method is not used by Jinja2 but should be
|
||||
implemented to allow applications to clear the bytecode cache used
|
||||
by a particular environment.
|
||||
"""
|
||||
|
||||
def get_cache_key(self, name, filename=None):
|
||||
"""Returns the unique hash key for this template name."""
|
||||
hash = sha1(name.encode('utf-8'))
|
||||
if filename is not None:
|
||||
filename = '|' + filename
|
||||
if isinstance(filename, text_type):
|
||||
filename = filename.encode('utf-8')
|
||||
hash.update(filename)
|
||||
return hash.hexdigest()
|
||||
|
||||
def get_source_checksum(self, source):
|
||||
"""Returns a checksum for the source."""
|
||||
return sha1(source.encode('utf-8')).hexdigest()
|
||||
|
||||
def get_bucket(self, environment, name, filename, source):
|
||||
"""Return a cache bucket for the given template. All arguments are
|
||||
mandatory but filename may be `None`.
|
||||
"""
|
||||
key = self.get_cache_key(name, filename)
|
||||
checksum = self.get_source_checksum(source)
|
||||
bucket = Bucket(environment, key, checksum)
|
||||
self.load_bytecode(bucket)
|
||||
return bucket
|
||||
|
||||
def set_bucket(self, bucket):
|
||||
"""Put the bucket into the cache."""
|
||||
self.dump_bytecode(bucket)
|
||||
|
||||
|
||||
class FileSystemBytecodeCache(BytecodeCache):
|
||||
"""A bytecode cache that stores bytecode on the filesystem. It accepts
|
||||
two arguments: The directory where the cache items are stored and a
|
||||
pattern string that is used to build the filename.
|
||||
|
||||
If no directory is specified the system temporary items folder is used.
|
||||
|
||||
The pattern can be used to have multiple separate caches operate on the
|
||||
same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
|
||||
is replaced with the cache key.
|
||||
|
||||
>>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
|
||||
|
||||
This bytecode cache supports clearing of the cache using the clear method.
|
||||
"""
|
||||
|
||||
def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
|
||||
if directory is None:
|
||||
directory = tempfile.gettempdir()
|
||||
self.directory = directory
|
||||
self.pattern = pattern
|
||||
|
||||
def _get_cache_filename(self, bucket):
|
||||
return path.join(self.directory, self.pattern % bucket.key)
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
f = open_if_exists(self._get_cache_filename(bucket), 'rb')
|
||||
if f is not None:
|
||||
try:
|
||||
bucket.load_bytecode(f)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def dump_bytecode(self, bucket):
|
||||
f = open(self._get_cache_filename(bucket), 'wb')
|
||||
try:
|
||||
bucket.write_bytecode(f)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def clear(self):
|
||||
# imported lazily here because google app-engine doesn't support
|
||||
# write access on the file system and the function does not exist
|
||||
# normally.
|
||||
from os import remove
|
||||
files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
|
||||
for filename in files:
|
||||
try:
|
||||
remove(path.join(self.directory, filename))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
class MemcachedBytecodeCache(BytecodeCache):
|
||||
"""This class implements a bytecode cache that uses a memcache cache for
|
||||
storing the information. It does not enforce a specific memcache library
|
||||
(tummy's memcache or cmemcache) but will accept any class that provides
|
||||
the minimal interface required.
|
||||
|
||||
Libraries compatible with this class:
|
||||
|
||||
- `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
|
||||
- `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
|
||||
- `cmemcache <http://gijsbert.org/cmemcache/>`_
|
||||
|
||||
(Unfortunately the django cache interface is not compatible because it
|
||||
does not support storing binary data, only unicode. You can however pass
|
||||
the underlying cache client to the bytecode cache which is available
|
||||
as `django.core.cache.cache._client`.)
|
||||
|
||||
The minimal interface for the client passed to the constructor is this:
|
||||
|
||||
.. class:: MinimalClientInterface
|
||||
|
||||
.. method:: set(key, value[, timeout])
|
||||
|
||||
Stores the bytecode in the cache. `value` is a string and
|
||||
`timeout` the timeout of the key. If timeout is not provided
|
||||
a default timeout or no timeout should be assumed, if it's
|
||||
provided it's an integer with the number of seconds the cache
|
||||
item should exist.
|
||||
|
||||
.. method:: get(key)
|
||||
|
||||
Returns the value for the cache key. If the item does not
|
||||
exist in the cache the return value must be `None`.
|
||||
|
||||
The other arguments to the constructor are the prefix for all keys that
|
||||
is added before the actual cache key and the timeout for the bytecode in
|
||||
the cache system. We recommend a high (or no) timeout.
|
||||
|
||||
This bytecode cache does not support clearing of used items in the cache.
|
||||
The clear method is a no-operation function.
|
||||
|
||||
.. versionadded:: 2.7
|
||||
Added support for ignoring memcache errors through the
|
||||
`ignore_memcache_errors` parameter.
|
||||
"""
|
||||
|
||||
def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
|
||||
ignore_memcache_errors=True):
|
||||
self.client = client
|
||||
self.prefix = prefix
|
||||
self.timeout = timeout
|
||||
self.ignore_memcache_errors = ignore_memcache_errors
|
||||
|
||||
def load_bytecode(self, bucket):
|
||||
try:
|
||||
code = self.client.get(self.prefix + bucket.key)
|
||||
except Exception:
|
||||
if not self.ignore_memcache_errors:
|
||||
raise
|
||||
code = None
|
||||
if code is not None:
|
||||
bucket.bytecode_from_string(code)
|
||||
|
||||
def dump_bytecode(self, bucket):
|
||||
args = (self.prefix + bucket.key, bucket.bytecode_to_string())
|
||||
if self.timeout is not None:
|
||||
args += (self.timeout,)
|
||||
try:
|
||||
self.client.set(*args)
|
||||
except Exception:
|
||||
if not self.ignore_memcache_errors:
|
||||
raise
|
||||
Vendored
-1640
File diff suppressed because it is too large
Load Diff
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja.constants
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Various constants.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
#: list of lorem ipsum words used by the lipsum() helper function
|
||||
LOREM_IPSUM_WORDS = u'''\
|
||||
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
|
||||
auctor augue bibendum blandit class commodo condimentum congue consectetuer
|
||||
consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
|
||||
diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
|
||||
elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
|
||||
faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
|
||||
hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
|
||||
justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
|
||||
luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
|
||||
mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
|
||||
nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
|
||||
penatibus per pharetra phasellus placerat platea porta porttitor posuere
|
||||
potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
|
||||
ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
|
||||
sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
|
||||
tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
|
||||
ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
|
||||
viverra volutpat vulputate'''
|
||||
Vendored
-337
@@ -1,337 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.debug
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Implements the debug interface for Jinja. This module does some pretty
|
||||
ugly stuff with the Python traceback system in order to achieve tracebacks
|
||||
with correct line numbers, locals and contents.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
from types import TracebackType
|
||||
from jinja2.utils import missing, internal_code
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
from jinja2._compat import iteritems, reraise, code_type
|
||||
|
||||
# on pypy we can take advantage of transparent proxies
|
||||
try:
|
||||
from __pypy__ import tproxy
|
||||
except ImportError:
|
||||
tproxy = None
|
||||
|
||||
|
||||
# how does the raise helper look like?
|
||||
try:
|
||||
exec("raise TypeError, 'foo'")
|
||||
except SyntaxError:
|
||||
raise_helper = 'raise __jinja_exception__[1]'
|
||||
except TypeError:
|
||||
raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
|
||||
|
||||
|
||||
class TracebackFrameProxy(object):
|
||||
"""Proxies a traceback frame."""
|
||||
|
||||
def __init__(self, tb):
|
||||
self.tb = tb
|
||||
self._tb_next = None
|
||||
|
||||
@property
|
||||
def tb_next(self):
|
||||
return self._tb_next
|
||||
|
||||
def set_next(self, next):
|
||||
if tb_set_next is not None:
|
||||
try:
|
||||
tb_set_next(self.tb, next and next.tb or None)
|
||||
except Exception:
|
||||
# this function can fail due to all the hackery it does
|
||||
# on various python implementations. We just catch errors
|
||||
# down and ignore them if necessary.
|
||||
pass
|
||||
self._tb_next = next
|
||||
|
||||
@property
|
||||
def is_jinja_frame(self):
|
||||
return '__jinja_template__' in self.tb.tb_frame.f_globals
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.tb, name)
|
||||
|
||||
|
||||
def make_frame_proxy(frame):
|
||||
proxy = TracebackFrameProxy(frame)
|
||||
if tproxy is None:
|
||||
return proxy
|
||||
def operation_handler(operation, *args, **kwargs):
|
||||
if operation in ('__getattribute__', '__getattr__'):
|
||||
return getattr(proxy, args[0])
|
||||
elif operation == '__setattr__':
|
||||
proxy.__setattr__(*args, **kwargs)
|
||||
else:
|
||||
return getattr(proxy, operation)(*args, **kwargs)
|
||||
return tproxy(TracebackType, operation_handler)
|
||||
|
||||
|
||||
class ProcessedTraceback(object):
|
||||
"""Holds a Jinja preprocessed traceback for printing or reraising."""
|
||||
|
||||
def __init__(self, exc_type, exc_value, frames):
|
||||
assert frames, 'no frames for this traceback?'
|
||||
self.exc_type = exc_type
|
||||
self.exc_value = exc_value
|
||||
self.frames = frames
|
||||
|
||||
# newly concatenate the frames (which are proxies)
|
||||
prev_tb = None
|
||||
for tb in self.frames:
|
||||
if prev_tb is not None:
|
||||
prev_tb.set_next(tb)
|
||||
prev_tb = tb
|
||||
prev_tb.set_next(None)
|
||||
|
||||
def render_as_text(self, limit=None):
|
||||
"""Return a string with the traceback."""
|
||||
lines = traceback.format_exception(self.exc_type, self.exc_value,
|
||||
self.frames[0], limit=limit)
|
||||
return ''.join(lines).rstrip()
|
||||
|
||||
def render_as_html(self, full=False):
|
||||
"""Return a unicode string with the traceback as rendered HTML."""
|
||||
from jinja2.debugrenderer import render_traceback
|
||||
return u'%s\n\n<!--\n%s\n-->' % (
|
||||
render_traceback(self, full=full),
|
||||
self.render_as_text().decode('utf-8', 'replace')
|
||||
)
|
||||
|
||||
@property
|
||||
def is_template_syntax_error(self):
|
||||
"""`True` if this is a template syntax error."""
|
||||
return isinstance(self.exc_value, TemplateSyntaxError)
|
||||
|
||||
@property
|
||||
def exc_info(self):
|
||||
"""Exception info tuple with a proxy around the frame objects."""
|
||||
return self.exc_type, self.exc_value, self.frames[0]
|
||||
|
||||
@property
|
||||
def standard_exc_info(self):
|
||||
"""Standard python exc_info for re-raising"""
|
||||
tb = self.frames[0]
|
||||
# the frame will be an actual traceback (or transparent proxy) if
|
||||
# we are on pypy or a python implementation with support for tproxy
|
||||
if type(tb) is not TracebackType:
|
||||
tb = tb.tb
|
||||
return self.exc_type, self.exc_value, tb
|
||||
|
||||
|
||||
def make_traceback(exc_info, source_hint=None):
|
||||
"""Creates a processed traceback object from the exc_info."""
|
||||
exc_type, exc_value, tb = exc_info
|
||||
if isinstance(exc_value, TemplateSyntaxError):
|
||||
exc_info = translate_syntax_error(exc_value, source_hint)
|
||||
initial_skip = 0
|
||||
else:
|
||||
initial_skip = 1
|
||||
return translate_exception(exc_info, initial_skip)
|
||||
|
||||
|
||||
def translate_syntax_error(error, source=None):
|
||||
"""Rewrites a syntax error to please traceback systems."""
|
||||
error.source = source
|
||||
error.translated = True
|
||||
exc_info = (error.__class__, error, None)
|
||||
filename = error.filename
|
||||
if filename is None:
|
||||
filename = '<unknown>'
|
||||
return fake_exc_info(exc_info, filename, error.lineno)
|
||||
|
||||
|
||||
def translate_exception(exc_info, initial_skip=0):
|
||||
"""If passed an exc_info it will automatically rewrite the exceptions
|
||||
all the way down to the correct line numbers and frames.
|
||||
"""
|
||||
tb = exc_info[2]
|
||||
frames = []
|
||||
|
||||
# skip some internal frames if wanted
|
||||
for x in range(initial_skip):
|
||||
if tb is not None:
|
||||
tb = tb.tb_next
|
||||
initial_tb = tb
|
||||
|
||||
while tb is not None:
|
||||
# skip frames decorated with @internalcode. These are internal
|
||||
# calls we can't avoid and that are useless in template debugging
|
||||
# output.
|
||||
if tb.tb_frame.f_code in internal_code:
|
||||
tb = tb.tb_next
|
||||
continue
|
||||
|
||||
# save a reference to the next frame if we override the current
|
||||
# one with a faked one.
|
||||
next = tb.tb_next
|
||||
|
||||
# fake template exceptions
|
||||
template = tb.tb_frame.f_globals.get('__jinja_template__')
|
||||
if template is not None:
|
||||
lineno = template.get_corresponding_lineno(tb.tb_lineno)
|
||||
tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
|
||||
lineno)[2]
|
||||
|
||||
frames.append(make_frame_proxy(tb))
|
||||
tb = next
|
||||
|
||||
# if we don't have any exceptions in the frames left, we have to
|
||||
# reraise it unchanged.
|
||||
# XXX: can we backup here? when could this happen?
|
||||
if not frames:
|
||||
reraise(exc_info[0], exc_info[1], exc_info[2])
|
||||
|
||||
return ProcessedTraceback(exc_info[0], exc_info[1], frames)
|
||||
|
||||
|
||||
def fake_exc_info(exc_info, filename, lineno):
|
||||
"""Helper for `translate_exception`."""
|
||||
exc_type, exc_value, tb = exc_info
|
||||
|
||||
# figure the real context out
|
||||
if tb is not None:
|
||||
real_locals = tb.tb_frame.f_locals.copy()
|
||||
ctx = real_locals.get('context')
|
||||
if ctx:
|
||||
locals = ctx.get_all()
|
||||
else:
|
||||
locals = {}
|
||||
for name, value in iteritems(real_locals):
|
||||
if name.startswith('l_') and value is not missing:
|
||||
locals[name[2:]] = value
|
||||
|
||||
# if there is a local called __jinja_exception__, we get
|
||||
# rid of it to not break the debug functionality.
|
||||
locals.pop('__jinja_exception__', None)
|
||||
else:
|
||||
locals = {}
|
||||
|
||||
# assamble fake globals we need
|
||||
globals = {
|
||||
'__name__': filename,
|
||||
'__file__': filename,
|
||||
'__jinja_exception__': exc_info[:2],
|
||||
|
||||
# we don't want to keep the reference to the template around
|
||||
# to not cause circular dependencies, but we mark it as Jinja
|
||||
# frame for the ProcessedTraceback
|
||||
'__jinja_template__': None
|
||||
}
|
||||
|
||||
# and fake the exception
|
||||
code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
|
||||
|
||||
# if it's possible, change the name of the code. This won't work
|
||||
# on some python environments such as google appengine
|
||||
try:
|
||||
if tb is None:
|
||||
location = 'template'
|
||||
else:
|
||||
function = tb.tb_frame.f_code.co_name
|
||||
if function == 'root':
|
||||
location = 'top-level template code'
|
||||
elif function.startswith('block_'):
|
||||
location = 'block "%s"' % function[6:]
|
||||
else:
|
||||
location = 'template'
|
||||
code = code_type(0, code.co_nlocals, code.co_stacksize,
|
||||
code.co_flags, code.co_code, code.co_consts,
|
||||
code.co_names, code.co_varnames, filename,
|
||||
location, code.co_firstlineno,
|
||||
code.co_lnotab, (), ())
|
||||
except:
|
||||
pass
|
||||
|
||||
# execute the code and catch the new traceback
|
||||
try:
|
||||
exec(code, globals, locals)
|
||||
except:
|
||||
exc_info = sys.exc_info()
|
||||
new_tb = exc_info[2].tb_next
|
||||
|
||||
# return without this frame
|
||||
return exc_info[:2] + (new_tb,)
|
||||
|
||||
|
||||
def _init_ugly_crap():
|
||||
"""This function implements a few ugly things so that we can patch the
|
||||
traceback objects. The function returned allows resetting `tb_next` on
|
||||
any python traceback object. Do not attempt to use this on non cpython
|
||||
interpreters
|
||||
"""
|
||||
import ctypes
|
||||
from types import TracebackType
|
||||
|
||||
# figure out side of _Py_ssize_t
|
||||
if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
|
||||
_Py_ssize_t = ctypes.c_int64
|
||||
else:
|
||||
_Py_ssize_t = ctypes.c_int
|
||||
|
||||
# regular python
|
||||
class _PyObject(ctypes.Structure):
|
||||
pass
|
||||
_PyObject._fields_ = [
|
||||
('ob_refcnt', _Py_ssize_t),
|
||||
('ob_type', ctypes.POINTER(_PyObject))
|
||||
]
|
||||
|
||||
# python with trace
|
||||
if hasattr(sys, 'getobjects'):
|
||||
class _PyObject(ctypes.Structure):
|
||||
pass
|
||||
_PyObject._fields_ = [
|
||||
('_ob_next', ctypes.POINTER(_PyObject)),
|
||||
('_ob_prev', ctypes.POINTER(_PyObject)),
|
||||
('ob_refcnt', _Py_ssize_t),
|
||||
('ob_type', ctypes.POINTER(_PyObject))
|
||||
]
|
||||
|
||||
class _Traceback(_PyObject):
|
||||
pass
|
||||
_Traceback._fields_ = [
|
||||
('tb_next', ctypes.POINTER(_Traceback)),
|
||||
('tb_frame', ctypes.POINTER(_PyObject)),
|
||||
('tb_lasti', ctypes.c_int),
|
||||
('tb_lineno', ctypes.c_int)
|
||||
]
|
||||
|
||||
def tb_set_next(tb, next):
|
||||
"""Set the tb_next attribute of a traceback object."""
|
||||
if not (isinstance(tb, TracebackType) and
|
||||
(next is None or isinstance(next, TracebackType))):
|
||||
raise TypeError('tb_set_next arguments must be traceback objects')
|
||||
obj = _Traceback.from_address(id(tb))
|
||||
if tb.tb_next is not None:
|
||||
old = _Traceback.from_address(id(tb.tb_next))
|
||||
old.ob_refcnt -= 1
|
||||
if next is None:
|
||||
obj.tb_next = ctypes.POINTER(_Traceback)()
|
||||
else:
|
||||
next = _Traceback.from_address(id(next))
|
||||
next.ob_refcnt += 1
|
||||
obj.tb_next = ctypes.pointer(next)
|
||||
|
||||
return tb_set_next
|
||||
|
||||
|
||||
# try to get a tb_set_next implementation if we don't have transparent
|
||||
# proxies.
|
||||
tb_set_next = None
|
||||
if tproxy is None:
|
||||
try:
|
||||
tb_set_next = _init_ugly_crap()
|
||||
except:
|
||||
pass
|
||||
del _init_ugly_crap
|
||||
Vendored
-43
@@ -1,43 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.defaults
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Jinja default filters and tags.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2._compat import range_type
|
||||
from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
|
||||
|
||||
|
||||
# defaults for the parser / lexer
|
||||
BLOCK_START_STRING = '{%'
|
||||
BLOCK_END_STRING = '%}'
|
||||
VARIABLE_START_STRING = '{{'
|
||||
VARIABLE_END_STRING = '}}'
|
||||
COMMENT_START_STRING = '{#'
|
||||
COMMENT_END_STRING = '#}'
|
||||
LINE_STATEMENT_PREFIX = None
|
||||
LINE_COMMENT_PREFIX = None
|
||||
TRIM_BLOCKS = False
|
||||
LSTRIP_BLOCKS = False
|
||||
NEWLINE_SEQUENCE = '\n'
|
||||
KEEP_TRAILING_NEWLINE = False
|
||||
|
||||
|
||||
# default filters, tests and namespace
|
||||
from jinja2.filters import FILTERS as DEFAULT_FILTERS
|
||||
from jinja2.tests import TESTS as DEFAULT_TESTS
|
||||
DEFAULT_NAMESPACE = {
|
||||
'range': range_type,
|
||||
'dict': lambda **kw: kw,
|
||||
'lipsum': generate_lorem_ipsum,
|
||||
'cycler': Cycler,
|
||||
'joiner': Joiner
|
||||
}
|
||||
|
||||
|
||||
# export all constants
|
||||
__all__ = tuple(x for x in locals().keys() if x.isupper())
|
||||
Vendored
-1191
File diff suppressed because it is too large
Load Diff
Vendored
-146
@@ -1,146 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.exceptions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Jinja exceptions.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2._compat import imap, text_type, PY2, implements_to_string
|
||||
|
||||
|
||||
class TemplateError(Exception):
|
||||
"""Baseclass for all template errors."""
|
||||
|
||||
if PY2:
|
||||
def __init__(self, message=None):
|
||||
if message is not None:
|
||||
message = text_type(message).encode('utf-8')
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
if self.args:
|
||||
message = self.args[0]
|
||||
if message is not None:
|
||||
return message.decode('utf-8', 'replace')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.message or u''
|
||||
else:
|
||||
def __init__(self, message=None):
|
||||
Exception.__init__(self, message)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
if self.args:
|
||||
message = self.args[0]
|
||||
if message is not None:
|
||||
return message
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class TemplateNotFound(IOError, LookupError, TemplateError):
|
||||
"""Raised if a template does not exist."""
|
||||
|
||||
# looks weird, but removes the warning descriptor that just
|
||||
# bogusly warns us about message being deprecated
|
||||
message = None
|
||||
|
||||
def __init__(self, name, message=None):
|
||||
IOError.__init__(self)
|
||||
if message is None:
|
||||
message = name
|
||||
self.message = message
|
||||
self.name = name
|
||||
self.templates = [name]
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
class TemplatesNotFound(TemplateNotFound):
|
||||
"""Like :class:`TemplateNotFound` but raised if multiple templates
|
||||
are selected. This is a subclass of :class:`TemplateNotFound`
|
||||
exception, so just catching the base exception will catch both.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
"""
|
||||
|
||||
def __init__(self, names=(), message=None):
|
||||
if message is None:
|
||||
message = u'none of the templates given were found: ' + \
|
||||
u', '.join(imap(text_type, names))
|
||||
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
||||
self.templates = list(names)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class TemplateSyntaxError(TemplateError):
|
||||
"""Raised to tell the user that there is a problem with the template."""
|
||||
|
||||
def __init__(self, message, lineno, name=None, filename=None):
|
||||
TemplateError.__init__(self, message)
|
||||
self.lineno = lineno
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
self.source = None
|
||||
|
||||
# this is set to True if the debug.translate_syntax_error
|
||||
# function translated the syntax error into a new traceback
|
||||
self.translated = False
|
||||
|
||||
def __str__(self):
|
||||
# for translated errors we only return the message
|
||||
if self.translated:
|
||||
return self.message
|
||||
|
||||
# otherwise attach some stuff
|
||||
location = 'line %d' % self.lineno
|
||||
name = self.filename or self.name
|
||||
if name:
|
||||
location = 'File "%s", %s' % (name, location)
|
||||
lines = [self.message, ' ' + location]
|
||||
|
||||
# if the source is set, add the line to the output
|
||||
if self.source is not None:
|
||||
try:
|
||||
line = self.source.splitlines()[self.lineno - 1]
|
||||
except IndexError:
|
||||
line = None
|
||||
if line:
|
||||
lines.append(' ' + line.strip())
|
||||
|
||||
return u'\n'.join(lines)
|
||||
|
||||
|
||||
class TemplateAssertionError(TemplateSyntaxError):
|
||||
"""Like a template syntax error, but covers cases where something in the
|
||||
template caused an error at compile time that wasn't necessarily caused
|
||||
by a syntax error. However it's a direct subclass of
|
||||
:exc:`TemplateSyntaxError` and has the same attributes.
|
||||
"""
|
||||
|
||||
|
||||
class TemplateRuntimeError(TemplateError):
|
||||
"""A generic runtime error in the template engine. Under some situations
|
||||
Jinja may raise this exception.
|
||||
"""
|
||||
|
||||
|
||||
class UndefinedError(TemplateRuntimeError):
|
||||
"""Raised if a template tries to operate on :class:`Undefined`."""
|
||||
|
||||
|
||||
class SecurityError(TemplateRuntimeError):
|
||||
"""Raised if a template tries to do something insecure if the
|
||||
sandbox is enabled.
|
||||
"""
|
||||
|
||||
|
||||
class FilterArgumentError(TemplateRuntimeError):
|
||||
"""This error is raised if a filter was called with inappropriate
|
||||
arguments
|
||||
"""
|
||||
Vendored
-636
@@ -1,636 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.ext
|
||||
~~~~~~~~~~
|
||||
|
||||
Jinja extensions allow to add custom tags similar to the way django custom
|
||||
tags work. By default two example extensions exist: an i18n and a cache
|
||||
extension.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.defaults import BLOCK_START_STRING, \
|
||||
BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
|
||||
COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
|
||||
LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
|
||||
KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.runtime import concat
|
||||
from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
|
||||
from jinja2.utils import contextfunction, import_string, Markup
|
||||
from jinja2._compat import next, with_metaclass, string_types, iteritems
|
||||
|
||||
|
||||
# the only real useful gettext functions for a Jinja template. Note
|
||||
# that ugettext must be assigned to gettext as Jinja doesn't support
|
||||
# non unicode strings.
|
||||
GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
|
||||
|
||||
|
||||
class ExtensionRegistry(type):
|
||||
"""Gives the extension an unique identifier."""
|
||||
|
||||
def __new__(cls, name, bases, d):
|
||||
rv = type.__new__(cls, name, bases, d)
|
||||
rv.identifier = rv.__module__ + '.' + rv.__name__
|
||||
return rv
|
||||
|
||||
|
||||
class Extension(with_metaclass(ExtensionRegistry, object)):
|
||||
"""Extensions can be used to add extra functionality to the Jinja template
|
||||
system at the parser level. Custom extensions are bound to an environment
|
||||
but may not store environment specific data on `self`. The reason for
|
||||
this is that an extension can be bound to another environment (for
|
||||
overlays) by creating a copy and reassigning the `environment` attribute.
|
||||
|
||||
As extensions are created by the environment they cannot accept any
|
||||
arguments for configuration. One may want to work around that by using
|
||||
a factory function, but that is not possible as extensions are identified
|
||||
by their import name. The correct way to configure the extension is
|
||||
storing the configuration values on the environment. Because this way the
|
||||
environment ends up acting as central configuration storage the
|
||||
attributes may clash which is why extensions have to ensure that the names
|
||||
they choose for configuration are not too generic. ``prefix`` for example
|
||||
is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
|
||||
name as includes the name of the extension (fragment cache).
|
||||
"""
|
||||
|
||||
#: if this extension parses this is the list of tags it's listening to.
|
||||
tags = set()
|
||||
|
||||
#: the priority of that extension. This is especially useful for
|
||||
#: extensions that preprocess values. A lower value means higher
|
||||
#: priority.
|
||||
#:
|
||||
#: .. versionadded:: 2.4
|
||||
priority = 100
|
||||
|
||||
def __init__(self, environment):
|
||||
self.environment = environment
|
||||
|
||||
def bind(self, environment):
|
||||
"""Create a copy of this extension bound to another environment."""
|
||||
rv = object.__new__(self.__class__)
|
||||
rv.__dict__.update(self.__dict__)
|
||||
rv.environment = environment
|
||||
return rv
|
||||
|
||||
def preprocess(self, source, name, filename=None):
|
||||
"""This method is called before the actual lexing and can be used to
|
||||
preprocess the source. The `filename` is optional. The return value
|
||||
must be the preprocessed source.
|
||||
"""
|
||||
return source
|
||||
|
||||
def filter_stream(self, stream):
|
||||
"""It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
|
||||
to filter tokens returned. This method has to return an iterable of
|
||||
:class:`~jinja2.lexer.Token`\s, but it doesn't have to return a
|
||||
:class:`~jinja2.lexer.TokenStream`.
|
||||
|
||||
In the `ext` folder of the Jinja2 source distribution there is a file
|
||||
called `inlinegettext.py` which implements a filter that utilizes this
|
||||
method.
|
||||
"""
|
||||
return stream
|
||||
|
||||
def parse(self, parser):
|
||||
"""If any of the :attr:`tags` matched this method is called with the
|
||||
parser as first argument. The token the parser stream is pointing at
|
||||
is the name token that matched. This method has to return one or a
|
||||
list of multiple nodes.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def attr(self, name, lineno=None):
|
||||
"""Return an attribute node for the current extension. This is useful
|
||||
to pass constants on extensions to generated template code.
|
||||
|
||||
::
|
||||
|
||||
self.attr('_my_attribute', lineno=lineno)
|
||||
"""
|
||||
return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
|
||||
|
||||
def call_method(self, name, args=None, kwargs=None, dyn_args=None,
|
||||
dyn_kwargs=None, lineno=None):
|
||||
"""Call a method of the extension. This is a shortcut for
|
||||
:meth:`attr` + :class:`jinja2.nodes.Call`.
|
||||
"""
|
||||
if args is None:
|
||||
args = []
|
||||
if kwargs is None:
|
||||
kwargs = []
|
||||
return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
|
||||
dyn_args, dyn_kwargs, lineno=lineno)
|
||||
|
||||
|
||||
@contextfunction
|
||||
def _gettext_alias(__context, *args, **kwargs):
|
||||
return __context.call(__context.resolve('gettext'), *args, **kwargs)
|
||||
|
||||
|
||||
def _make_new_gettext(func):
|
||||
@contextfunction
|
||||
def gettext(__context, __string, **variables):
|
||||
rv = __context.call(func, __string)
|
||||
if __context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
return rv % variables
|
||||
return gettext
|
||||
|
||||
|
||||
def _make_new_ngettext(func):
|
||||
@contextfunction
|
||||
def ngettext(__context, __singular, __plural, __num, **variables):
|
||||
variables.setdefault('num', __num)
|
||||
rv = __context.call(func, __singular, __plural, __num)
|
||||
if __context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
return rv % variables
|
||||
return ngettext
|
||||
|
||||
|
||||
class InternationalizationExtension(Extension):
|
||||
"""This extension adds gettext support to Jinja2."""
|
||||
tags = set(['trans'])
|
||||
|
||||
# TODO: the i18n extension is currently reevaluating values in a few
|
||||
# situations. Take this example:
|
||||
# {% trans count=something() %}{{ count }} foo{% pluralize
|
||||
# %}{{ count }} fooss{% endtrans %}
|
||||
# something is called twice here. One time for the gettext value and
|
||||
# the other time for the n-parameter of the ngettext function.
|
||||
|
||||
def __init__(self, environment):
|
||||
Extension.__init__(self, environment)
|
||||
environment.globals['_'] = _gettext_alias
|
||||
environment.extend(
|
||||
install_gettext_translations=self._install,
|
||||
install_null_translations=self._install_null,
|
||||
install_gettext_callables=self._install_callables,
|
||||
uninstall_gettext_translations=self._uninstall,
|
||||
extract_translations=self._extract,
|
||||
newstyle_gettext=False
|
||||
)
|
||||
|
||||
def _install(self, translations, newstyle=None):
|
||||
gettext = getattr(translations, 'ugettext', None)
|
||||
if gettext is None:
|
||||
gettext = translations.gettext
|
||||
ngettext = getattr(translations, 'ungettext', None)
|
||||
if ngettext is None:
|
||||
ngettext = translations.ngettext
|
||||
self._install_callables(gettext, ngettext, newstyle)
|
||||
|
||||
def _install_null(self, newstyle=None):
|
||||
self._install_callables(
|
||||
lambda x: x,
|
||||
lambda s, p, n: (n != 1 and (p,) or (s,))[0],
|
||||
newstyle
|
||||
)
|
||||
|
||||
def _install_callables(self, gettext, ngettext, newstyle=None):
|
||||
if newstyle is not None:
|
||||
self.environment.newstyle_gettext = newstyle
|
||||
if self.environment.newstyle_gettext:
|
||||
gettext = _make_new_gettext(gettext)
|
||||
ngettext = _make_new_ngettext(ngettext)
|
||||
self.environment.globals.update(
|
||||
gettext=gettext,
|
||||
ngettext=ngettext
|
||||
)
|
||||
|
||||
def _uninstall(self, translations):
|
||||
for key in 'gettext', 'ngettext':
|
||||
self.environment.globals.pop(key, None)
|
||||
|
||||
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
|
||||
if isinstance(source, string_types):
|
||||
source = self.environment.parse(source)
|
||||
return extract_from_ast(source, gettext_functions)
|
||||
|
||||
def parse(self, parser):
|
||||
"""Parse a translatable tag."""
|
||||
lineno = next(parser.stream).lineno
|
||||
num_called_num = False
|
||||
|
||||
# find all the variables referenced. Additionally a variable can be
|
||||
# defined in the body of the trans block too, but this is checked at
|
||||
# a later state.
|
||||
plural_expr = None
|
||||
plural_expr_assignment = None
|
||||
variables = {}
|
||||
while parser.stream.current.type != 'block_end':
|
||||
if variables:
|
||||
parser.stream.expect('comma')
|
||||
|
||||
# skip colon for python compatibility
|
||||
if parser.stream.skip_if('colon'):
|
||||
break
|
||||
|
||||
name = parser.stream.expect('name')
|
||||
if name.value in variables:
|
||||
parser.fail('translatable variable %r defined twice.' %
|
||||
name.value, name.lineno,
|
||||
exc=TemplateAssertionError)
|
||||
|
||||
# expressions
|
||||
if parser.stream.current.type == 'assign':
|
||||
next(parser.stream)
|
||||
variables[name.value] = var = parser.parse_expression()
|
||||
else:
|
||||
variables[name.value] = var = nodes.Name(name.value, 'load')
|
||||
|
||||
if plural_expr is None:
|
||||
if isinstance(var, nodes.Call):
|
||||
plural_expr = nodes.Name('_trans', 'load')
|
||||
variables[name.value] = plural_expr
|
||||
plural_expr_assignment = nodes.Assign(
|
||||
nodes.Name('_trans', 'store'), var)
|
||||
else:
|
||||
plural_expr = var
|
||||
num_called_num = name.value == 'num'
|
||||
|
||||
parser.stream.expect('block_end')
|
||||
|
||||
plural = plural_names = None
|
||||
have_plural = False
|
||||
referenced = set()
|
||||
|
||||
# now parse until endtrans or pluralize
|
||||
singular_names, singular = self._parse_block(parser, True)
|
||||
if singular_names:
|
||||
referenced.update(singular_names)
|
||||
if plural_expr is None:
|
||||
plural_expr = nodes.Name(singular_names[0], 'load')
|
||||
num_called_num = singular_names[0] == 'num'
|
||||
|
||||
# if we have a pluralize block, we parse that too
|
||||
if parser.stream.current.test('name:pluralize'):
|
||||
have_plural = True
|
||||
next(parser.stream)
|
||||
if parser.stream.current.type != 'block_end':
|
||||
name = parser.stream.expect('name')
|
||||
if name.value not in variables:
|
||||
parser.fail('unknown variable %r for pluralization' %
|
||||
name.value, name.lineno,
|
||||
exc=TemplateAssertionError)
|
||||
plural_expr = variables[name.value]
|
||||
num_called_num = name.value == 'num'
|
||||
parser.stream.expect('block_end')
|
||||
plural_names, plural = self._parse_block(parser, False)
|
||||
next(parser.stream)
|
||||
referenced.update(plural_names)
|
||||
else:
|
||||
next(parser.stream)
|
||||
|
||||
# register free names as simple name expressions
|
||||
for var in referenced:
|
||||
if var not in variables:
|
||||
variables[var] = nodes.Name(var, 'load')
|
||||
|
||||
if not have_plural:
|
||||
plural_expr = None
|
||||
elif plural_expr is None:
|
||||
parser.fail('pluralize without variables', lineno)
|
||||
|
||||
node = self._make_node(singular, plural, variables, plural_expr,
|
||||
bool(referenced),
|
||||
num_called_num and have_plural)
|
||||
node.set_lineno(lineno)
|
||||
if plural_expr_assignment is not None:
|
||||
return [plural_expr_assignment, node]
|
||||
else:
|
||||
return node
|
||||
|
||||
def _parse_block(self, parser, allow_pluralize):
|
||||
"""Parse until the next block tag with a given name."""
|
||||
referenced = []
|
||||
buf = []
|
||||
while 1:
|
||||
if parser.stream.current.type == 'data':
|
||||
buf.append(parser.stream.current.value.replace('%', '%%'))
|
||||
next(parser.stream)
|
||||
elif parser.stream.current.type == 'variable_begin':
|
||||
next(parser.stream)
|
||||
name = parser.stream.expect('name').value
|
||||
referenced.append(name)
|
||||
buf.append('%%(%s)s' % name)
|
||||
parser.stream.expect('variable_end')
|
||||
elif parser.stream.current.type == 'block_begin':
|
||||
next(parser.stream)
|
||||
if parser.stream.current.test('name:endtrans'):
|
||||
break
|
||||
elif parser.stream.current.test('name:pluralize'):
|
||||
if allow_pluralize:
|
||||
break
|
||||
parser.fail('a translatable section can have only one '
|
||||
'pluralize section')
|
||||
parser.fail('control structures in translatable sections are '
|
||||
'not allowed')
|
||||
elif parser.stream.eos:
|
||||
parser.fail('unclosed translation block')
|
||||
else:
|
||||
assert False, 'internal parser error'
|
||||
|
||||
return referenced, concat(buf)
|
||||
|
||||
def _make_node(self, singular, plural, variables, plural_expr,
|
||||
vars_referenced, num_called_num):
|
||||
"""Generates a useful node from the data provided."""
|
||||
# no variables referenced? no need to escape for old style
|
||||
# gettext invocations only if there are vars.
|
||||
if not vars_referenced and not self.environment.newstyle_gettext:
|
||||
singular = singular.replace('%%', '%')
|
||||
if plural:
|
||||
plural = plural.replace('%%', '%')
|
||||
|
||||
# singular only:
|
||||
if plural_expr is None:
|
||||
gettext = nodes.Name('gettext', 'load')
|
||||
node = nodes.Call(gettext, [nodes.Const(singular)],
|
||||
[], None, None)
|
||||
|
||||
# singular and plural
|
||||
else:
|
||||
ngettext = nodes.Name('ngettext', 'load')
|
||||
node = nodes.Call(ngettext, [
|
||||
nodes.Const(singular),
|
||||
nodes.Const(plural),
|
||||
plural_expr
|
||||
], [], None, None)
|
||||
|
||||
# in case newstyle gettext is used, the method is powerful
|
||||
# enough to handle the variable expansion and autoescape
|
||||
# handling itself
|
||||
if self.environment.newstyle_gettext:
|
||||
for key, value in iteritems(variables):
|
||||
# the function adds that later anyways in case num was
|
||||
# called num, so just skip it.
|
||||
if num_called_num and key == 'num':
|
||||
continue
|
||||
node.kwargs.append(nodes.Keyword(key, value))
|
||||
|
||||
# otherwise do that here
|
||||
else:
|
||||
# mark the return value as safe if we are in an
|
||||
# environment with autoescaping turned on
|
||||
node = nodes.MarkSafeIfAutoescape(node)
|
||||
if variables:
|
||||
node = nodes.Mod(node, nodes.Dict([
|
||||
nodes.Pair(nodes.Const(key), value)
|
||||
for key, value in variables.items()
|
||||
]))
|
||||
return nodes.Output([node])
|
||||
|
||||
|
||||
class ExprStmtExtension(Extension):
|
||||
"""Adds a `do` tag to Jinja2 that works like the print statement just
|
||||
that it doesn't print the return value.
|
||||
"""
|
||||
tags = set(['do'])
|
||||
|
||||
def parse(self, parser):
|
||||
node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
|
||||
node.node = parser.parse_tuple()
|
||||
return node
|
||||
|
||||
|
||||
class LoopControlExtension(Extension):
|
||||
"""Adds break and continue to the template engine."""
|
||||
tags = set(['break', 'continue'])
|
||||
|
||||
def parse(self, parser):
|
||||
token = next(parser.stream)
|
||||
if token.value == 'break':
|
||||
return nodes.Break(lineno=token.lineno)
|
||||
return nodes.Continue(lineno=token.lineno)
|
||||
|
||||
|
||||
class WithExtension(Extension):
|
||||
"""Adds support for a django-like with block."""
|
||||
tags = set(['with'])
|
||||
|
||||
def parse(self, parser):
|
||||
node = nodes.Scope(lineno=next(parser.stream).lineno)
|
||||
assignments = []
|
||||
while parser.stream.current.type != 'block_end':
|
||||
lineno = parser.stream.current.lineno
|
||||
if assignments:
|
||||
parser.stream.expect('comma')
|
||||
target = parser.parse_assign_target()
|
||||
parser.stream.expect('assign')
|
||||
expr = parser.parse_expression()
|
||||
assignments.append(nodes.Assign(target, expr, lineno=lineno))
|
||||
node.body = assignments + \
|
||||
list(parser.parse_statements(('name:endwith',),
|
||||
drop_needle=True))
|
||||
return node
|
||||
|
||||
|
||||
class AutoEscapeExtension(Extension):
|
||||
"""Changes auto escape rules for a scope."""
|
||||
tags = set(['autoescape'])
|
||||
|
||||
def parse(self, parser):
|
||||
node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno)
|
||||
node.options = [
|
||||
nodes.Keyword('autoescape', parser.parse_expression())
|
||||
]
|
||||
node.body = parser.parse_statements(('name:endautoescape',),
|
||||
drop_needle=True)
|
||||
return nodes.Scope([node])
|
||||
|
||||
|
||||
def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
|
||||
babel_style=True):
|
||||
"""Extract localizable strings from the given template node. Per
|
||||
default this function returns matches in babel style that means non string
|
||||
parameters as well as keyword arguments are returned as `None`. This
|
||||
allows Babel to figure out what you really meant if you are using
|
||||
gettext functions that allow keyword arguments for placeholder expansion.
|
||||
If you don't want that behavior set the `babel_style` parameter to `False`
|
||||
which causes only strings to be returned and parameters are always stored
|
||||
in tuples. As a consequence invalid gettext calls (calls without a single
|
||||
string parameter or string parameters after non-string parameters) are
|
||||
skipped.
|
||||
|
||||
This example explains the behavior:
|
||||
|
||||
>>> from jinja2 import Environment
|
||||
>>> env = Environment()
|
||||
>>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
|
||||
>>> list(extract_from_ast(node))
|
||||
[(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
|
||||
>>> list(extract_from_ast(node, babel_style=False))
|
||||
[(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
|
||||
|
||||
For every string found this function yields a ``(lineno, function,
|
||||
message)`` tuple, where:
|
||||
|
||||
* ``lineno`` is the number of the line on which the string was found,
|
||||
* ``function`` is the name of the ``gettext`` function used (if the
|
||||
string was extracted from embedded Python code), and
|
||||
* ``message`` is the string itself (a ``unicode`` object, or a tuple
|
||||
of ``unicode`` objects for functions with multiple string arguments).
|
||||
|
||||
This extraction function operates on the AST and is because of that unable
|
||||
to extract any comments. For comment support you have to use the babel
|
||||
extraction interface or extract comments yourself.
|
||||
"""
|
||||
for node in node.find_all(nodes.Call):
|
||||
if not isinstance(node.node, nodes.Name) or \
|
||||
node.node.name not in gettext_functions:
|
||||
continue
|
||||
|
||||
strings = []
|
||||
for arg in node.args:
|
||||
if isinstance(arg, nodes.Const) and \
|
||||
isinstance(arg.value, string_types):
|
||||
strings.append(arg.value)
|
||||
else:
|
||||
strings.append(None)
|
||||
|
||||
for arg in node.kwargs:
|
||||
strings.append(None)
|
||||
if node.dyn_args is not None:
|
||||
strings.append(None)
|
||||
if node.dyn_kwargs is not None:
|
||||
strings.append(None)
|
||||
|
||||
if not babel_style:
|
||||
strings = tuple(x for x in strings if x is not None)
|
||||
if not strings:
|
||||
continue
|
||||
else:
|
||||
if len(strings) == 1:
|
||||
strings = strings[0]
|
||||
else:
|
||||
strings = tuple(strings)
|
||||
yield node.lineno, node.node.name, strings
|
||||
|
||||
|
||||
class _CommentFinder(object):
|
||||
"""Helper class to find comments in a token stream. Can only
|
||||
find comments for gettext calls forwards. Once the comment
|
||||
from line 4 is found, a comment for line 1 will not return a
|
||||
usable value.
|
||||
"""
|
||||
|
||||
def __init__(self, tokens, comment_tags):
|
||||
self.tokens = tokens
|
||||
self.comment_tags = comment_tags
|
||||
self.offset = 0
|
||||
self.last_lineno = 0
|
||||
|
||||
def find_backwards(self, offset):
|
||||
try:
|
||||
for _, token_type, token_value in \
|
||||
reversed(self.tokens[self.offset:offset]):
|
||||
if token_type in ('comment', 'linecomment'):
|
||||
try:
|
||||
prefix, comment = token_value.split(None, 1)
|
||||
except ValueError:
|
||||
continue
|
||||
if prefix in self.comment_tags:
|
||||
return [comment.rstrip()]
|
||||
return []
|
||||
finally:
|
||||
self.offset = offset
|
||||
|
||||
def find_comments(self, lineno):
|
||||
if not self.comment_tags or self.last_lineno > lineno:
|
||||
return []
|
||||
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
|
||||
if token_lineno > lineno:
|
||||
return self.find_backwards(self.offset + idx)
|
||||
return self.find_backwards(len(self.tokens))
|
||||
|
||||
|
||||
def babel_extract(fileobj, keywords, comment_tags, options):
|
||||
"""Babel extraction method for Jinja templates.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
Basic support for translation comments was added. If `comment_tags`
|
||||
is now set to a list of keywords for extraction, the extractor will
|
||||
try to find the best preceeding comment that begins with one of the
|
||||
keywords. For best results, make sure to not have more than one
|
||||
gettext call in one line of code and the matching comment in the
|
||||
same line or the line before.
|
||||
|
||||
.. versionchanged:: 2.5.1
|
||||
The `newstyle_gettext` flag can be set to `True` to enable newstyle
|
||||
gettext calls.
|
||||
|
||||
.. versionchanged:: 2.7
|
||||
A `silent` option can now be provided. If set to `False` template
|
||||
syntax errors are propagated instead of being ignored.
|
||||
|
||||
:param fileobj: the file-like object the messages should be extracted from
|
||||
:param keywords: a list of keywords (i.e. function names) that should be
|
||||
recognized as translation functions
|
||||
:param comment_tags: a list of translator tags to search for and include
|
||||
in the results.
|
||||
:param options: a dictionary of additional options (optional)
|
||||
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
|
||||
(comments will be empty currently)
|
||||
"""
|
||||
extensions = set()
|
||||
for extension in options.get('extensions', '').split(','):
|
||||
extension = extension.strip()
|
||||
if not extension:
|
||||
continue
|
||||
extensions.add(import_string(extension))
|
||||
if InternationalizationExtension not in extensions:
|
||||
extensions.add(InternationalizationExtension)
|
||||
|
||||
def getbool(options, key, default=False):
|
||||
return options.get(key, str(default)).lower() in \
|
||||
('1', 'on', 'yes', 'true')
|
||||
|
||||
silent = getbool(options, 'silent', True)
|
||||
environment = Environment(
|
||||
options.get('block_start_string', BLOCK_START_STRING),
|
||||
options.get('block_end_string', BLOCK_END_STRING),
|
||||
options.get('variable_start_string', VARIABLE_START_STRING),
|
||||
options.get('variable_end_string', VARIABLE_END_STRING),
|
||||
options.get('comment_start_string', COMMENT_START_STRING),
|
||||
options.get('comment_end_string', COMMENT_END_STRING),
|
||||
options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
|
||||
options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
|
||||
getbool(options, 'trim_blocks', TRIM_BLOCKS),
|
||||
getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS),
|
||||
NEWLINE_SEQUENCE,
|
||||
getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE),
|
||||
frozenset(extensions),
|
||||
cache_size=0,
|
||||
auto_reload=False
|
||||
)
|
||||
|
||||
if getbool(options, 'newstyle_gettext'):
|
||||
environment.newstyle_gettext = True
|
||||
|
||||
source = fileobj.read().decode(options.get('encoding', 'utf-8'))
|
||||
try:
|
||||
node = environment.parse(source)
|
||||
tokens = list(environment.lex(environment.preprocess(source)))
|
||||
except TemplateSyntaxError as e:
|
||||
if not silent:
|
||||
raise
|
||||
# skip templates with syntax errors
|
||||
return
|
||||
|
||||
finder = _CommentFinder(tokens, comment_tags)
|
||||
for lineno, func, message in extract_from_ast(node, keywords):
|
||||
yield lineno, func, message, finder.find_comments(lineno)
|
||||
|
||||
|
||||
#: nicer import names
|
||||
i18n = InternationalizationExtension
|
||||
do = ExprStmtExtension
|
||||
loopcontrols = LoopControlExtension
|
||||
with_ = WithExtension
|
||||
autoescape = AutoEscapeExtension
|
||||
Vendored
-987
@@ -1,987 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.filters
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Bundled jinja filters.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
import math
|
||||
|
||||
from random import choice
|
||||
from operator import itemgetter
|
||||
from itertools import groupby
|
||||
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
||||
unicode_urlencode
|
||||
from jinja2.runtime import Undefined
|
||||
from jinja2.exceptions import FilterArgumentError
|
||||
from jinja2._compat import next, imap, string_types, text_type, iteritems
|
||||
|
||||
|
||||
_word_re = re.compile(r'\w+(?u)')
|
||||
|
||||
|
||||
def contextfilter(f):
|
||||
"""Decorator for marking context dependent filters. The current
|
||||
:class:`Context` will be passed as first argument.
|
||||
"""
|
||||
f.contextfilter = True
|
||||
return f
|
||||
|
||||
|
||||
def evalcontextfilter(f):
|
||||
"""Decorator for marking eval-context dependent filters. An eval
|
||||
context object is passed as first argument. For more information
|
||||
about the eval context, see :ref:`eval-context`.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
f.evalcontextfilter = True
|
||||
return f
|
||||
|
||||
|
||||
def environmentfilter(f):
|
||||
"""Decorator for marking evironment dependent filters. The current
|
||||
:class:`Environment` is passed to the filter as first argument.
|
||||
"""
|
||||
f.environmentfilter = True
|
||||
return f
|
||||
|
||||
|
||||
def make_attrgetter(environment, attribute):
|
||||
"""Returns a callable that looks up the given attribute from a
|
||||
passed object with the rules of the environment. Dots are allowed
|
||||
to access attributes of attributes. Integer parts in paths are
|
||||
looked up as integers.
|
||||
"""
|
||||
if not isinstance(attribute, string_types) \
|
||||
or ('.' not in attribute and not attribute.isdigit()):
|
||||
return lambda x: environment.getitem(x, attribute)
|
||||
attribute = attribute.split('.')
|
||||
def attrgetter(item):
|
||||
for part in attribute:
|
||||
if part.isdigit():
|
||||
part = int(part)
|
||||
item = environment.getitem(item, part)
|
||||
return item
|
||||
return attrgetter
|
||||
|
||||
|
||||
def do_forceescape(value):
|
||||
"""Enforce HTML escaping. This will probably double escape variables."""
|
||||
if hasattr(value, '__html__'):
|
||||
value = value.__html__()
|
||||
return escape(text_type(value))
|
||||
|
||||
|
||||
def do_urlencode(value):
|
||||
"""Escape strings for use in URLs (uses UTF-8 encoding). It accepts both
|
||||
dictionaries and regular strings as well as pairwise iterables.
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
itemiter = None
|
||||
if isinstance(value, dict):
|
||||
itemiter = iteritems(value)
|
||||
elif not isinstance(value, string_types):
|
||||
try:
|
||||
itemiter = iter(value)
|
||||
except TypeError:
|
||||
pass
|
||||
if itemiter is None:
|
||||
return unicode_urlencode(value)
|
||||
return u'&'.join(unicode_urlencode(k) + '=' +
|
||||
unicode_urlencode(v) for k, v in itemiter)
|
||||
|
||||
|
||||
@evalcontextfilter
|
||||
def do_replace(eval_ctx, s, old, new, count=None):
|
||||
"""Return a copy of the value with all occurrences of a substring
|
||||
replaced with a new one. The first argument is the substring
|
||||
that should be replaced, the second is the replacement string.
|
||||
If the optional third argument ``count`` is given, only the first
|
||||
``count`` occurrences are replaced:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ "Hello World"|replace("Hello", "Goodbye") }}
|
||||
-> Goodbye World
|
||||
|
||||
{{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
|
||||
-> d'oh, d'oh, aaargh
|
||||
"""
|
||||
if count is None:
|
||||
count = -1
|
||||
if not eval_ctx.autoescape:
|
||||
return text_type(s).replace(text_type(old), text_type(new), count)
|
||||
if hasattr(old, '__html__') or hasattr(new, '__html__') and \
|
||||
not hasattr(s, '__html__'):
|
||||
s = escape(s)
|
||||
else:
|
||||
s = soft_unicode(s)
|
||||
return s.replace(soft_unicode(old), soft_unicode(new), count)
|
||||
|
||||
|
||||
def do_upper(s):
|
||||
"""Convert a value to uppercase."""
|
||||
return soft_unicode(s).upper()
|
||||
|
||||
|
||||
def do_lower(s):
|
||||
"""Convert a value to lowercase."""
|
||||
return soft_unicode(s).lower()
|
||||
|
||||
|
||||
@evalcontextfilter
|
||||
def do_xmlattr(_eval_ctx, d, autospace=True):
|
||||
"""Create an SGML/XML attribute string based on the items in a dict.
|
||||
All values that are neither `none` nor `undefined` are automatically
|
||||
escaped:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<ul{{ {'class': 'my_list', 'missing': none,
|
||||
'id': 'list-%d'|format(variable)}|xmlattr }}>
|
||||
...
|
||||
</ul>
|
||||
|
||||
Results in something like this:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<ul class="my_list" id="list-42">
|
||||
...
|
||||
</ul>
|
||||
|
||||
As you can see it automatically prepends a space in front of the item
|
||||
if the filter returned something unless the second parameter is false.
|
||||
"""
|
||||
rv = u' '.join(
|
||||
u'%s="%s"' % (escape(key), escape(value))
|
||||
for key, value in iteritems(d)
|
||||
if value is not None and not isinstance(value, Undefined)
|
||||
)
|
||||
if autospace and rv:
|
||||
rv = u' ' + rv
|
||||
if _eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
return rv
|
||||
|
||||
|
||||
def do_capitalize(s):
|
||||
"""Capitalize a value. The first character will be uppercase, all others
|
||||
lowercase.
|
||||
"""
|
||||
return soft_unicode(s).capitalize()
|
||||
|
||||
|
||||
def do_title(s):
|
||||
"""Return a titlecased version of the value. I.e. words will start with
|
||||
uppercase letters, all remaining characters are lowercase.
|
||||
"""
|
||||
rv = []
|
||||
for item in re.compile(r'([-\s]+)(?u)').split(s):
|
||||
if not item:
|
||||
continue
|
||||
rv.append(item[0].upper() + item[1:].lower())
|
||||
return ''.join(rv)
|
||||
|
||||
|
||||
def do_dictsort(value, case_sensitive=False, by='key'):
|
||||
"""Sort a dict and yield (key, value) pairs. Because python dicts are
|
||||
unsorted you may want to use this function to order them by either
|
||||
key or value:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% for item in mydict|dictsort %}
|
||||
sort the dict by key, case insensitive
|
||||
|
||||
{% for item in mydict|dictsort(true) %}
|
||||
sort the dict by key, case sensitive
|
||||
|
||||
{% for item in mydict|dictsort(false, 'value') %}
|
||||
sort the dict by key, case insensitive, sorted
|
||||
normally and ordered by value.
|
||||
"""
|
||||
if by == 'key':
|
||||
pos = 0
|
||||
elif by == 'value':
|
||||
pos = 1
|
||||
else:
|
||||
raise FilterArgumentError('You can only sort by either '
|
||||
'"key" or "value"')
|
||||
def sort_func(item):
|
||||
value = item[pos]
|
||||
if isinstance(value, string_types) and not case_sensitive:
|
||||
value = value.lower()
|
||||
return value
|
||||
|
||||
return sorted(value.items(), key=sort_func)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_sort(environment, value, reverse=False, case_sensitive=False,
|
||||
attribute=None):
|
||||
"""Sort an iterable. Per default it sorts ascending, if you pass it
|
||||
true as first argument it will reverse the sorting.
|
||||
|
||||
If the iterable is made of strings the third parameter can be used to
|
||||
control the case sensitiveness of the comparison which is disabled by
|
||||
default.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% for item in iterable|sort %}
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
It is also possible to sort by an attribute (for example to sort
|
||||
by the date of an object) by specifying the `attribute` parameter:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% for item in iterable|sort(attribute='date') %}
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
The `attribute` parameter was added.
|
||||
"""
|
||||
if not case_sensitive:
|
||||
def sort_func(item):
|
||||
if isinstance(item, string_types):
|
||||
item = item.lower()
|
||||
return item
|
||||
else:
|
||||
sort_func = None
|
||||
if attribute is not None:
|
||||
getter = make_attrgetter(environment, attribute)
|
||||
def sort_func(item, processor=sort_func or (lambda x: x)):
|
||||
return processor(getter(item))
|
||||
return sorted(value, key=sort_func, reverse=reverse)
|
||||
|
||||
|
||||
def do_default(value, default_value=u'', boolean=False):
|
||||
"""If the value is undefined it will return the passed default value,
|
||||
otherwise the value of the variable:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ my_variable|default('my_variable is not defined') }}
|
||||
|
||||
This will output the value of ``my_variable`` if the variable was
|
||||
defined, otherwise ``'my_variable is not defined'``. If you want
|
||||
to use default with variables that evaluate to false you have to
|
||||
set the second parameter to `true`:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ ''|default('the string was empty', true) }}
|
||||
"""
|
||||
if isinstance(value, Undefined) or (boolean and not value):
|
||||
return default_value
|
||||
return value
|
||||
|
||||
|
||||
@evalcontextfilter
|
||||
def do_join(eval_ctx, value, d=u'', attribute=None):
|
||||
"""Return a string which is the concatenation of the strings in the
|
||||
sequence. The separator between elements is an empty string per
|
||||
default, you can define it with the optional parameter:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ [1, 2, 3]|join('|') }}
|
||||
-> 1|2|3
|
||||
|
||||
{{ [1, 2, 3]|join }}
|
||||
-> 123
|
||||
|
||||
It is also possible to join certain attributes of an object:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ users|join(', ', attribute='username') }}
|
||||
|
||||
.. versionadded:: 2.6
|
||||
The `attribute` parameter was added.
|
||||
"""
|
||||
if attribute is not None:
|
||||
value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
|
||||
|
||||
# no automatic escaping? joining is a lot eaiser then
|
||||
if not eval_ctx.autoescape:
|
||||
return text_type(d).join(imap(text_type, value))
|
||||
|
||||
# if the delimiter doesn't have an html representation we check
|
||||
# if any of the items has. If yes we do a coercion to Markup
|
||||
if not hasattr(d, '__html__'):
|
||||
value = list(value)
|
||||
do_escape = False
|
||||
for idx, item in enumerate(value):
|
||||
if hasattr(item, '__html__'):
|
||||
do_escape = True
|
||||
else:
|
||||
value[idx] = text_type(item)
|
||||
if do_escape:
|
||||
d = escape(d)
|
||||
else:
|
||||
d = text_type(d)
|
||||
return d.join(value)
|
||||
|
||||
# no html involved, to normal joining
|
||||
return soft_unicode(d).join(imap(soft_unicode, value))
|
||||
|
||||
|
||||
def do_center(value, width=80):
|
||||
"""Centers the value in a field of a given width."""
|
||||
return text_type(value).center(width)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_first(environment, seq):
|
||||
"""Return the first item of a sequence."""
|
||||
try:
|
||||
return next(iter(seq))
|
||||
except StopIteration:
|
||||
return environment.undefined('No first item, sequence was empty.')
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_last(environment, seq):
|
||||
"""Return the last item of a sequence."""
|
||||
try:
|
||||
return next(iter(reversed(seq)))
|
||||
except StopIteration:
|
||||
return environment.undefined('No last item, sequence was empty.')
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_random(environment, seq):
|
||||
"""Return a random item from the sequence."""
|
||||
try:
|
||||
return choice(seq)
|
||||
except IndexError:
|
||||
return environment.undefined('No random item, sequence was empty.')
|
||||
|
||||
|
||||
def do_filesizeformat(value, binary=False):
|
||||
"""Format the value like a 'human-readable' file size (i.e. 13 kB,
|
||||
4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
|
||||
Giga, etc.), if the second parameter is set to `True` the binary
|
||||
prefixes are used (Mebi, Gibi).
|
||||
"""
|
||||
bytes = float(value)
|
||||
base = binary and 1024 or 1000
|
||||
prefixes = [
|
||||
(binary and 'KiB' or 'kB'),
|
||||
(binary and 'MiB' or 'MB'),
|
||||
(binary and 'GiB' or 'GB'),
|
||||
(binary and 'TiB' or 'TB'),
|
||||
(binary and 'PiB' or 'PB'),
|
||||
(binary and 'EiB' or 'EB'),
|
||||
(binary and 'ZiB' or 'ZB'),
|
||||
(binary and 'YiB' or 'YB')
|
||||
]
|
||||
if bytes == 1:
|
||||
return '1 Byte'
|
||||
elif bytes < base:
|
||||
return '%d Bytes' % bytes
|
||||
else:
|
||||
for i, prefix in enumerate(prefixes):
|
||||
unit = base ** (i + 2)
|
||||
if bytes < unit:
|
||||
return '%.1f %s' % ((base * bytes / unit), prefix)
|
||||
return '%.1f %s' % ((base * bytes / unit), prefix)
|
||||
|
||||
|
||||
def do_pprint(value, verbose=False):
|
||||
"""Pretty print a variable. Useful for debugging.
|
||||
|
||||
With Jinja 1.2 onwards you can pass it a parameter. If this parameter
|
||||
is truthy the output will be more verbose (this requires `pretty`)
|
||||
"""
|
||||
return pformat(value, verbose=verbose)
|
||||
|
||||
|
||||
@evalcontextfilter
|
||||
def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
|
||||
"""Converts URLs in plain text into clickable links.
|
||||
|
||||
If you pass the filter an additional integer it will shorten the urls
|
||||
to that number. Also a third argument exists that makes the urls
|
||||
"nofollow":
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ mytext|urlize(40, true) }}
|
||||
links are shortened to 40 chars and defined with rel="nofollow"
|
||||
"""
|
||||
rv = urlize(value, trim_url_limit, nofollow)
|
||||
if eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
return rv
|
||||
|
||||
|
||||
def do_indent(s, width=4, indentfirst=False):
|
||||
"""Return a copy of the passed string, each line indented by
|
||||
4 spaces. The first line is not indented. If you want to
|
||||
change the number of spaces or indent the first line too
|
||||
you can pass additional parameters to the filter:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ mytext|indent(2, true) }}
|
||||
indent by two spaces and indent the first line too.
|
||||
"""
|
||||
indention = u' ' * width
|
||||
rv = (u'\n' + indention).join(s.splitlines())
|
||||
if indentfirst:
|
||||
rv = indention + rv
|
||||
return rv
|
||||
|
||||
|
||||
def do_truncate(s, length=255, killwords=False, end='...'):
|
||||
"""Return a truncated copy of the string. The length is specified
|
||||
with the first parameter which defaults to ``255``. If the second
|
||||
parameter is ``true`` the filter will cut the text at length. Otherwise
|
||||
it will discard the last word. If the text was in fact
|
||||
truncated it will append an ellipsis sign (``"..."``). If you want a
|
||||
different ellipsis sign than ``"..."`` you can specify it using the
|
||||
third parameter.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ "foo bar"|truncate(5) }}
|
||||
-> "foo ..."
|
||||
{{ "foo bar"|truncate(5, True) }}
|
||||
-> "foo b..."
|
||||
"""
|
||||
if len(s) <= length:
|
||||
return s
|
||||
elif killwords:
|
||||
return s[:length] + end
|
||||
words = s.split(' ')
|
||||
result = []
|
||||
m = 0
|
||||
for word in words:
|
||||
m += len(word) + 1
|
||||
if m > length:
|
||||
break
|
||||
result.append(word)
|
||||
result.append(end)
|
||||
return u' '.join(result)
|
||||
|
||||
@environmentfilter
|
||||
def do_wordwrap(environment, s, width=79, break_long_words=True,
|
||||
wrapstring=None):
|
||||
"""
|
||||
Return a copy of the string passed to the filter wrapped after
|
||||
``79`` characters. You can override this default using the first
|
||||
parameter. If you set the second parameter to `false` Jinja will not
|
||||
split words apart if they are longer than `width`. By default, the newlines
|
||||
will be the default newlines for the environment, but this can be changed
|
||||
using the wrapstring keyword argument.
|
||||
|
||||
.. versionadded:: 2.7
|
||||
Added support for the `wrapstring` parameter.
|
||||
"""
|
||||
if not wrapstring:
|
||||
wrapstring = environment.newline_sequence
|
||||
import textwrap
|
||||
return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False,
|
||||
replace_whitespace=False,
|
||||
break_long_words=break_long_words))
|
||||
|
||||
|
||||
def do_wordcount(s):
|
||||
"""Count the words in that string."""
|
||||
return len(_word_re.findall(s))
|
||||
|
||||
|
||||
def do_int(value, default=0):
|
||||
"""Convert the value into an integer. If the
|
||||
conversion doesn't work it will return ``0``. You can
|
||||
override this default using the first parameter.
|
||||
"""
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
# this quirk is necessary so that "42.23"|int gives 42.
|
||||
try:
|
||||
return int(float(value))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def do_float(value, default=0.0):
|
||||
"""Convert the value into a floating point number. If the
|
||||
conversion doesn't work it will return ``0.0``. You can
|
||||
override this default using the first parameter.
|
||||
"""
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def do_format(value, *args, **kwargs):
|
||||
"""
|
||||
Apply python string formatting on an object:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ "%s - %s"|format("Hello?", "Foo!") }}
|
||||
-> Hello? - Foo!
|
||||
"""
|
||||
if args and kwargs:
|
||||
raise FilterArgumentError('can\'t handle positional and keyword '
|
||||
'arguments at the same time')
|
||||
return soft_unicode(value) % (kwargs or args)
|
||||
|
||||
|
||||
def do_trim(value):
|
||||
"""Strip leading and trailing whitespace."""
|
||||
return soft_unicode(value).strip()
|
||||
|
||||
|
||||
def do_striptags(value):
|
||||
"""Strip SGML/XML tags and replace adjacent whitespace by one space.
|
||||
"""
|
||||
if hasattr(value, '__html__'):
|
||||
value = value.__html__()
|
||||
return Markup(text_type(value)).striptags()
|
||||
|
||||
|
||||
def do_slice(value, slices, fill_with=None):
|
||||
"""Slice an iterator and return a list of lists containing
|
||||
those items. Useful if you want to create a div containing
|
||||
three ul tags that represent columns:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<div class="columwrapper">
|
||||
{%- for column in items|slice(3) %}
|
||||
<ul class="column-{{ loop.index }}">
|
||||
{%- for item in column %}
|
||||
<li>{{ item }}</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- endfor %}
|
||||
</div>
|
||||
|
||||
If you pass it a second argument it's used to fill missing
|
||||
values on the last iteration.
|
||||
"""
|
||||
seq = list(value)
|
||||
length = len(seq)
|
||||
items_per_slice = length // slices
|
||||
slices_with_extra = length % slices
|
||||
offset = 0
|
||||
for slice_number in range(slices):
|
||||
start = offset + slice_number * items_per_slice
|
||||
if slice_number < slices_with_extra:
|
||||
offset += 1
|
||||
end = offset + (slice_number + 1) * items_per_slice
|
||||
tmp = seq[start:end]
|
||||
if fill_with is not None and slice_number >= slices_with_extra:
|
||||
tmp.append(fill_with)
|
||||
yield tmp
|
||||
|
||||
|
||||
def do_batch(value, linecount, fill_with=None):
|
||||
"""
|
||||
A filter that batches items. It works pretty much like `slice`
|
||||
just the other way round. It returns a list of lists with the
|
||||
given number of items. If you provide a second parameter this
|
||||
is used to fill up missing items. See this example:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<table>
|
||||
{%- for row in items|batch(3, ' ') %}
|
||||
<tr>
|
||||
{%- for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
"""
|
||||
result = []
|
||||
tmp = []
|
||||
for item in value:
|
||||
if len(tmp) == linecount:
|
||||
yield tmp
|
||||
tmp = []
|
||||
tmp.append(item)
|
||||
if tmp:
|
||||
if fill_with is not None and len(tmp) < linecount:
|
||||
tmp += [fill_with] * (linecount - len(tmp))
|
||||
yield tmp
|
||||
|
||||
|
||||
def do_round(value, precision=0, method='common'):
|
||||
"""Round the number to a given precision. The first
|
||||
parameter specifies the precision (default is ``0``), the
|
||||
second the rounding method:
|
||||
|
||||
- ``'common'`` rounds either up or down
|
||||
- ``'ceil'`` always rounds up
|
||||
- ``'floor'`` always rounds down
|
||||
|
||||
If you don't specify a method ``'common'`` is used.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ 42.55|round }}
|
||||
-> 43.0
|
||||
{{ 42.55|round(1, 'floor') }}
|
||||
-> 42.5
|
||||
|
||||
Note that even if rounded to 0 precision, a float is returned. If
|
||||
you need a real integer, pipe it through `int`:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ 42.55|round|int }}
|
||||
-> 43
|
||||
"""
|
||||
if not method in ('common', 'ceil', 'floor'):
|
||||
raise FilterArgumentError('method must be common, ceil or floor')
|
||||
if method == 'common':
|
||||
return round(value, precision)
|
||||
func = getattr(math, method)
|
||||
return func(value * (10 ** precision)) / (10 ** precision)
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_groupby(environment, value, attribute):
|
||||
"""Group a sequence of objects by a common attribute.
|
||||
|
||||
If you for example have a list of dicts or objects that represent persons
|
||||
with `gender`, `first_name` and `last_name` attributes and you want to
|
||||
group all users by genders you can do something like the following
|
||||
snippet:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<ul>
|
||||
{% for group in persons|groupby('gender') %}
|
||||
<li>{{ group.grouper }}<ul>
|
||||
{% for person in group.list %}
|
||||
<li>{{ person.first_name }} {{ person.last_name }}</li>
|
||||
{% endfor %}</ul></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Additionally it's possible to use tuple unpacking for the grouper and
|
||||
list:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<ul>
|
||||
{% for grouper, list in persons|groupby('gender') %}
|
||||
...
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
As you can see the item we're grouping by is stored in the `grouper`
|
||||
attribute and the `list` contains all the objects that have this grouper
|
||||
in common.
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
It's now possible to use dotted notation to group by the child
|
||||
attribute of another attribute.
|
||||
"""
|
||||
expr = make_attrgetter(environment, attribute)
|
||||
return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
|
||||
|
||||
|
||||
class _GroupTuple(tuple):
|
||||
__slots__ = ()
|
||||
grouper = property(itemgetter(0))
|
||||
list = property(itemgetter(1))
|
||||
|
||||
def __new__(cls, xxx_todo_changeme):
|
||||
(key, value) = xxx_todo_changeme
|
||||
return tuple.__new__(cls, (key, list(value)))
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_sum(environment, iterable, attribute=None, start=0):
|
||||
"""Returns the sum of a sequence of numbers plus the value of parameter
|
||||
'start' (which defaults to 0). When the sequence is empty it returns
|
||||
start.
|
||||
|
||||
It is also possible to sum up only certain attributes:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
Total: {{ items|sum(attribute='price') }}
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
The `attribute` parameter was added to allow suming up over
|
||||
attributes. Also the `start` parameter was moved on to the right.
|
||||
"""
|
||||
if attribute is not None:
|
||||
iterable = imap(make_attrgetter(environment, attribute), iterable)
|
||||
return sum(iterable, start)
|
||||
|
||||
|
||||
def do_list(value):
|
||||
"""Convert the value into a list. If it was a string the returned list
|
||||
will be a list of characters.
|
||||
"""
|
||||
return list(value)
|
||||
|
||||
|
||||
def do_mark_safe(value):
|
||||
"""Mark the value as safe which means that in an environment with automatic
|
||||
escaping enabled this variable will not be escaped.
|
||||
"""
|
||||
return Markup(value)
|
||||
|
||||
|
||||
def do_mark_unsafe(value):
|
||||
"""Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
|
||||
return text_type(value)
|
||||
|
||||
|
||||
def do_reverse(value):
|
||||
"""Reverse the object or return an iterator the iterates over it the other
|
||||
way round.
|
||||
"""
|
||||
if isinstance(value, string_types):
|
||||
return value[::-1]
|
||||
try:
|
||||
return reversed(value)
|
||||
except TypeError:
|
||||
try:
|
||||
rv = list(value)
|
||||
rv.reverse()
|
||||
return rv
|
||||
except TypeError:
|
||||
raise FilterArgumentError('argument must be iterable')
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def do_attr(environment, obj, name):
|
||||
"""Get an attribute of an object. ``foo|attr("bar")`` works like
|
||||
``foo["bar"]`` just that always an attribute is returned and items are not
|
||||
looked up.
|
||||
|
||||
See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
|
||||
"""
|
||||
try:
|
||||
name = str(name)
|
||||
except UnicodeError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
value = getattr(obj, name)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if environment.sandboxed and not \
|
||||
environment.is_safe_attribute(obj, name, value):
|
||||
return environment.unsafe_undefined(obj, name)
|
||||
return value
|
||||
return environment.undefined(obj=obj, name=name)
|
||||
|
||||
|
||||
@contextfilter
|
||||
def do_map(*args, **kwargs):
|
||||
"""Applies a filter on a sequence of objects or looks up an attribute.
|
||||
This is useful when dealing with lists of objects but you are really
|
||||
only interested in a certain value of it.
|
||||
|
||||
The basic usage is mapping on an attribute. Imagine you have a list
|
||||
of users but you are only interested in a list of usernames:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
Users on this page: {{ users|map(attribute='username')|join(', ') }}
|
||||
|
||||
Alternatively you can let it invoke a filter by passing the name of the
|
||||
filter and the arguments afterwards. A good example would be applying a
|
||||
text conversion filter on a sequence:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
Users on this page: {{ titles|map('lower')|join(', ') }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
context = args[0]
|
||||
seq = args[1]
|
||||
|
||||
if len(args) == 2 and 'attribute' in kwargs:
|
||||
attribute = kwargs.pop('attribute')
|
||||
if kwargs:
|
||||
raise FilterArgumentError('Unexpected keyword argument %r' %
|
||||
next(iter(kwargs)))
|
||||
func = make_attrgetter(context.environment, attribute)
|
||||
else:
|
||||
try:
|
||||
name = args[2]
|
||||
args = args[3:]
|
||||
except LookupError:
|
||||
raise FilterArgumentError('map requires a filter argument')
|
||||
func = lambda item: context.environment.call_filter(
|
||||
name, item, args, kwargs, context=context)
|
||||
|
||||
if seq:
|
||||
for item in seq:
|
||||
yield func(item)
|
||||
|
||||
|
||||
@contextfilter
|
||||
def do_select(*args, **kwargs):
|
||||
"""Filters a sequence of objects by appying a test to either the object
|
||||
or the attribute and only selecting the ones with the test succeeding.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ numbers|select("odd") }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return _select_or_reject(args, kwargs, lambda x: x, False)
|
||||
|
||||
|
||||
@contextfilter
|
||||
def do_reject(*args, **kwargs):
|
||||
"""Filters a sequence of objects by appying a test to either the object
|
||||
or the attribute and rejecting the ones with the test succeeding.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ numbers|reject("odd") }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return _select_or_reject(args, kwargs, lambda x: not x, False)
|
||||
|
||||
|
||||
@contextfilter
|
||||
def do_selectattr(*args, **kwargs):
|
||||
"""Filters a sequence of objects by appying a test to either the object
|
||||
or the attribute and only selecting the ones with the test succeeding.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ users|selectattr("is_active") }}
|
||||
{{ users|selectattr("email", "none") }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return _select_or_reject(args, kwargs, lambda x: x, True)
|
||||
|
||||
|
||||
@contextfilter
|
||||
def do_rejectattr(*args, **kwargs):
|
||||
"""Filters a sequence of objects by appying a test to either the object
|
||||
or the attribute and rejecting the ones with the test succeeding.
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{{ users|rejectattr("is_active") }}
|
||||
{{ users|rejectattr("email", "none") }}
|
||||
|
||||
.. versionadded:: 2.7
|
||||
"""
|
||||
return _select_or_reject(args, kwargs, lambda x: not x, True)
|
||||
|
||||
|
||||
def _select_or_reject(args, kwargs, modfunc, lookup_attr):
|
||||
context = args[0]
|
||||
seq = args[1]
|
||||
if lookup_attr:
|
||||
try:
|
||||
attr = args[2]
|
||||
except LookupError:
|
||||
raise FilterArgumentError('Missing parameter for attribute name')
|
||||
transfunc = make_attrgetter(context.environment, attr)
|
||||
off = 1
|
||||
else:
|
||||
off = 0
|
||||
transfunc = lambda x: x
|
||||
|
||||
try:
|
||||
name = args[2 + off]
|
||||
args = args[3 + off:]
|
||||
func = lambda item: context.environment.call_test(
|
||||
name, item, args, kwargs)
|
||||
except LookupError:
|
||||
func = bool
|
||||
|
||||
if seq:
|
||||
for item in seq:
|
||||
if modfunc(func(transfunc(item))):
|
||||
yield item
|
||||
|
||||
|
||||
FILTERS = {
|
||||
'attr': do_attr,
|
||||
'replace': do_replace,
|
||||
'upper': do_upper,
|
||||
'lower': do_lower,
|
||||
'escape': escape,
|
||||
'e': escape,
|
||||
'forceescape': do_forceescape,
|
||||
'capitalize': do_capitalize,
|
||||
'title': do_title,
|
||||
'default': do_default,
|
||||
'd': do_default,
|
||||
'join': do_join,
|
||||
'count': len,
|
||||
'dictsort': do_dictsort,
|
||||
'sort': do_sort,
|
||||
'length': len,
|
||||
'reverse': do_reverse,
|
||||
'center': do_center,
|
||||
'indent': do_indent,
|
||||
'title': do_title,
|
||||
'capitalize': do_capitalize,
|
||||
'first': do_first,
|
||||
'last': do_last,
|
||||
'map': do_map,
|
||||
'random': do_random,
|
||||
'reject': do_reject,
|
||||
'rejectattr': do_rejectattr,
|
||||
'filesizeformat': do_filesizeformat,
|
||||
'pprint': do_pprint,
|
||||
'truncate': do_truncate,
|
||||
'wordwrap': do_wordwrap,
|
||||
'wordcount': do_wordcount,
|
||||
'int': do_int,
|
||||
'float': do_float,
|
||||
'string': soft_unicode,
|
||||
'list': do_list,
|
||||
'urlize': do_urlize,
|
||||
'format': do_format,
|
||||
'trim': do_trim,
|
||||
'striptags': do_striptags,
|
||||
'select': do_select,
|
||||
'selectattr': do_selectattr,
|
||||
'slice': do_slice,
|
||||
'batch': do_batch,
|
||||
'sum': do_sum,
|
||||
'abs': abs,
|
||||
'round': do_round,
|
||||
'groupby': do_groupby,
|
||||
'safe': do_mark_safe,
|
||||
'xmlattr': do_xmlattr,
|
||||
'urlencode': do_urlencode
|
||||
}
|
||||
Vendored
-733
@@ -1,733 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.lexer
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module implements a Jinja / Python combination lexer. The
|
||||
`Lexer` class provided by this module is used to do some preprocessing
|
||||
for Jinja.
|
||||
|
||||
On the one hand it filters out invalid operators like the bitshift
|
||||
operators we don't allow in templates. On the other hand it separates
|
||||
template code and python code in expressions.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
|
||||
from operator import itemgetter
|
||||
from collections import deque
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
from jinja2.utils import LRUCache
|
||||
from jinja2._compat import next, iteritems, implements_iterator, text_type, \
|
||||
intern
|
||||
|
||||
|
||||
# cache for the lexers. Exists in order to be able to have multiple
|
||||
# environments with the same lexer
|
||||
_lexer_cache = LRUCache(50)
|
||||
|
||||
# static regular expressions
|
||||
whitespace_re = re.compile(r'\s+', re.U)
|
||||
string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||
integer_re = re.compile(r'\d+')
|
||||
|
||||
# we use the unicode identifier rule if this python version is able
|
||||
# to handle unicode identifiers, otherwise the standard ASCII one.
|
||||
try:
|
||||
compile('föö', '<unknown>', 'eval')
|
||||
except SyntaxError:
|
||||
name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
|
||||
else:
|
||||
from jinja2 import _stringdefs
|
||||
name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
|
||||
_stringdefs.xid_continue))
|
||||
|
||||
float_re = re.compile(r'(?<!\.)\d+\.\d+')
|
||||
newline_re = re.compile(r'(\r\n|\r|\n)')
|
||||
|
||||
# internal the tokens and keep references to them
|
||||
TOKEN_ADD = intern('add')
|
||||
TOKEN_ASSIGN = intern('assign')
|
||||
TOKEN_COLON = intern('colon')
|
||||
TOKEN_COMMA = intern('comma')
|
||||
TOKEN_DIV = intern('div')
|
||||
TOKEN_DOT = intern('dot')
|
||||
TOKEN_EQ = intern('eq')
|
||||
TOKEN_FLOORDIV = intern('floordiv')
|
||||
TOKEN_GT = intern('gt')
|
||||
TOKEN_GTEQ = intern('gteq')
|
||||
TOKEN_LBRACE = intern('lbrace')
|
||||
TOKEN_LBRACKET = intern('lbracket')
|
||||
TOKEN_LPAREN = intern('lparen')
|
||||
TOKEN_LT = intern('lt')
|
||||
TOKEN_LTEQ = intern('lteq')
|
||||
TOKEN_MOD = intern('mod')
|
||||
TOKEN_MUL = intern('mul')
|
||||
TOKEN_NE = intern('ne')
|
||||
TOKEN_PIPE = intern('pipe')
|
||||
TOKEN_POW = intern('pow')
|
||||
TOKEN_RBRACE = intern('rbrace')
|
||||
TOKEN_RBRACKET = intern('rbracket')
|
||||
TOKEN_RPAREN = intern('rparen')
|
||||
TOKEN_SEMICOLON = intern('semicolon')
|
||||
TOKEN_SUB = intern('sub')
|
||||
TOKEN_TILDE = intern('tilde')
|
||||
TOKEN_WHITESPACE = intern('whitespace')
|
||||
TOKEN_FLOAT = intern('float')
|
||||
TOKEN_INTEGER = intern('integer')
|
||||
TOKEN_NAME = intern('name')
|
||||
TOKEN_STRING = intern('string')
|
||||
TOKEN_OPERATOR = intern('operator')
|
||||
TOKEN_BLOCK_BEGIN = intern('block_begin')
|
||||
TOKEN_BLOCK_END = intern('block_end')
|
||||
TOKEN_VARIABLE_BEGIN = intern('variable_begin')
|
||||
TOKEN_VARIABLE_END = intern('variable_end')
|
||||
TOKEN_RAW_BEGIN = intern('raw_begin')
|
||||
TOKEN_RAW_END = intern('raw_end')
|
||||
TOKEN_COMMENT_BEGIN = intern('comment_begin')
|
||||
TOKEN_COMMENT_END = intern('comment_end')
|
||||
TOKEN_COMMENT = intern('comment')
|
||||
TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
|
||||
TOKEN_LINESTATEMENT_END = intern('linestatement_end')
|
||||
TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
|
||||
TOKEN_LINECOMMENT_END = intern('linecomment_end')
|
||||
TOKEN_LINECOMMENT = intern('linecomment')
|
||||
TOKEN_DATA = intern('data')
|
||||
TOKEN_INITIAL = intern('initial')
|
||||
TOKEN_EOF = intern('eof')
|
||||
|
||||
# bind operators to token types
|
||||
operators = {
|
||||
'+': TOKEN_ADD,
|
||||
'-': TOKEN_SUB,
|
||||
'/': TOKEN_DIV,
|
||||
'//': TOKEN_FLOORDIV,
|
||||
'*': TOKEN_MUL,
|
||||
'%': TOKEN_MOD,
|
||||
'**': TOKEN_POW,
|
||||
'~': TOKEN_TILDE,
|
||||
'[': TOKEN_LBRACKET,
|
||||
']': TOKEN_RBRACKET,
|
||||
'(': TOKEN_LPAREN,
|
||||
')': TOKEN_RPAREN,
|
||||
'{': TOKEN_LBRACE,
|
||||
'}': TOKEN_RBRACE,
|
||||
'==': TOKEN_EQ,
|
||||
'!=': TOKEN_NE,
|
||||
'>': TOKEN_GT,
|
||||
'>=': TOKEN_GTEQ,
|
||||
'<': TOKEN_LT,
|
||||
'<=': TOKEN_LTEQ,
|
||||
'=': TOKEN_ASSIGN,
|
||||
'.': TOKEN_DOT,
|
||||
':': TOKEN_COLON,
|
||||
'|': TOKEN_PIPE,
|
||||
',': TOKEN_COMMA,
|
||||
';': TOKEN_SEMICOLON
|
||||
}
|
||||
|
||||
reverse_operators = dict([(v, k) for k, v in iteritems(operators)])
|
||||
assert len(operators) == len(reverse_operators), 'operators dropped'
|
||||
operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
|
||||
sorted(operators, key=lambda x: -len(x))))
|
||||
|
||||
ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
|
||||
TOKEN_COMMENT_END, TOKEN_WHITESPACE,
|
||||
TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN,
|
||||
TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT])
|
||||
ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
|
||||
TOKEN_COMMENT, TOKEN_LINECOMMENT])
|
||||
|
||||
|
||||
def _describe_token_type(token_type):
|
||||
if token_type in reverse_operators:
|
||||
return reverse_operators[token_type]
|
||||
return {
|
||||
TOKEN_COMMENT_BEGIN: 'begin of comment',
|
||||
TOKEN_COMMENT_END: 'end of comment',
|
||||
TOKEN_COMMENT: 'comment',
|
||||
TOKEN_LINECOMMENT: 'comment',
|
||||
TOKEN_BLOCK_BEGIN: 'begin of statement block',
|
||||
TOKEN_BLOCK_END: 'end of statement block',
|
||||
TOKEN_VARIABLE_BEGIN: 'begin of print statement',
|
||||
TOKEN_VARIABLE_END: 'end of print statement',
|
||||
TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
|
||||
TOKEN_LINESTATEMENT_END: 'end of line statement',
|
||||
TOKEN_DATA: 'template data / text',
|
||||
TOKEN_EOF: 'end of template'
|
||||
}.get(token_type, token_type)
|
||||
|
||||
|
||||
def describe_token(token):
|
||||
"""Returns a description of the token."""
|
||||
if token.type == 'name':
|
||||
return token.value
|
||||
return _describe_token_type(token.type)
|
||||
|
||||
|
||||
def describe_token_expr(expr):
|
||||
"""Like `describe_token` but for token expressions."""
|
||||
if ':' in expr:
|
||||
type, value = expr.split(':', 1)
|
||||
if type == 'name':
|
||||
return value
|
||||
else:
|
||||
type = expr
|
||||
return _describe_token_type(type)
|
||||
|
||||
|
||||
def count_newlines(value):
|
||||
"""Count the number of newline characters in the string. This is
|
||||
useful for extensions that filter a stream.
|
||||
"""
|
||||
return len(newline_re.findall(value))
|
||||
|
||||
|
||||
def compile_rules(environment):
|
||||
"""Compiles all the rules from the environment into a list of rules."""
|
||||
e = re.escape
|
||||
rules = [
|
||||
(len(environment.comment_start_string), 'comment',
|
||||
e(environment.comment_start_string)),
|
||||
(len(environment.block_start_string), 'block',
|
||||
e(environment.block_start_string)),
|
||||
(len(environment.variable_start_string), 'variable',
|
||||
e(environment.variable_start_string))
|
||||
]
|
||||
|
||||
if environment.line_statement_prefix is not None:
|
||||
rules.append((len(environment.line_statement_prefix), 'linestatement',
|
||||
r'^[ \t\v]*' + e(environment.line_statement_prefix)))
|
||||
if environment.line_comment_prefix is not None:
|
||||
rules.append((len(environment.line_comment_prefix), 'linecomment',
|
||||
r'(?:^|(?<=\S))[^\S\r\n]*' +
|
||||
e(environment.line_comment_prefix)))
|
||||
|
||||
return [x[1:] for x in sorted(rules, reverse=True)]
|
||||
|
||||
|
||||
class Failure(object):
|
||||
"""Class that raises a `TemplateSyntaxError` if called.
|
||||
Used by the `Lexer` to specify known errors.
|
||||
"""
|
||||
|
||||
def __init__(self, message, cls=TemplateSyntaxError):
|
||||
self.message = message
|
||||
self.error_class = cls
|
||||
|
||||
def __call__(self, lineno, filename):
|
||||
raise self.error_class(self.message, lineno, filename)
|
||||
|
||||
|
||||
class Token(tuple):
|
||||
"""Token class."""
|
||||
__slots__ = ()
|
||||
lineno, type, value = (property(itemgetter(x)) for x in range(3))
|
||||
|
||||
def __new__(cls, lineno, type, value):
|
||||
return tuple.__new__(cls, (lineno, intern(str(type)), value))
|
||||
|
||||
def __str__(self):
|
||||
if self.type in reverse_operators:
|
||||
return reverse_operators[self.type]
|
||||
elif self.type == 'name':
|
||||
return self.value
|
||||
return self.type
|
||||
|
||||
def test(self, expr):
|
||||
"""Test a token against a token expression. This can either be a
|
||||
token type or ``'token_type:token_value'``. This can only test
|
||||
against string values and types.
|
||||
"""
|
||||
# here we do a regular string equality check as test_any is usually
|
||||
# passed an iterable of not interned strings.
|
||||
if self.type == expr:
|
||||
return True
|
||||
elif ':' in expr:
|
||||
return expr.split(':', 1) == [self.type, self.value]
|
||||
return False
|
||||
|
||||
def test_any(self, *iterable):
|
||||
"""Test against multiple token expressions."""
|
||||
for expr in iterable:
|
||||
if self.test(expr):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return 'Token(%r, %r, %r)' % (
|
||||
self.lineno,
|
||||
self.type,
|
||||
self.value
|
||||
)
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class TokenStreamIterator(object):
|
||||
"""The iterator for tokenstreams. Iterate over the stream
|
||||
until the eof token is reached.
|
||||
"""
|
||||
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
token = self.stream.current
|
||||
if token.type is TOKEN_EOF:
|
||||
self.stream.close()
|
||||
raise StopIteration()
|
||||
next(self.stream)
|
||||
return token
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class TokenStream(object):
|
||||
"""A token stream is an iterable that yields :class:`Token`\s. The
|
||||
parser however does not iterate over it but calls :meth:`next` to go
|
||||
one token ahead. The current active token is stored as :attr:`current`.
|
||||
"""
|
||||
|
||||
def __init__(self, generator, name, filename):
|
||||
self._iter = iter(generator)
|
||||
self._pushed = deque()
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
self.current = Token(1, TOKEN_INITIAL, '')
|
||||
next(self)
|
||||
|
||||
def __iter__(self):
|
||||
return TokenStreamIterator(self)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._pushed) or self.current.type is not TOKEN_EOF
|
||||
__nonzero__ = __bool__ # py2
|
||||
|
||||
eos = property(lambda x: not x, doc="Are we at the end of the stream?")
|
||||
|
||||
def push(self, token):
|
||||
"""Push a token back to the stream."""
|
||||
self._pushed.append(token)
|
||||
|
||||
def look(self):
|
||||
"""Look at the next token."""
|
||||
old_token = next(self)
|
||||
result = self.current
|
||||
self.push(result)
|
||||
self.current = old_token
|
||||
return result
|
||||
|
||||
def skip(self, n=1):
|
||||
"""Got n tokens ahead."""
|
||||
for x in range(n):
|
||||
next(self)
|
||||
|
||||
def next_if(self, expr):
|
||||
"""Perform the token test and return the token if it matched.
|
||||
Otherwise the return value is `None`.
|
||||
"""
|
||||
if self.current.test(expr):
|
||||
return next(self)
|
||||
|
||||
def skip_if(self, expr):
|
||||
"""Like :meth:`next_if` but only returns `True` or `False`."""
|
||||
return self.next_if(expr) is not None
|
||||
|
||||
def __next__(self):
|
||||
"""Go one token ahead and return the old one"""
|
||||
rv = self.current
|
||||
if self._pushed:
|
||||
self.current = self._pushed.popleft()
|
||||
elif self.current.type is not TOKEN_EOF:
|
||||
try:
|
||||
self.current = next(self._iter)
|
||||
except StopIteration:
|
||||
self.close()
|
||||
return rv
|
||||
|
||||
def close(self):
|
||||
"""Close the stream."""
|
||||
self.current = Token(self.current.lineno, TOKEN_EOF, '')
|
||||
self._iter = None
|
||||
self.closed = True
|
||||
|
||||
def expect(self, expr):
|
||||
"""Expect a given token type and return it. This accepts the same
|
||||
argument as :meth:`jinja2.lexer.Token.test`.
|
||||
"""
|
||||
if not self.current.test(expr):
|
||||
expr = describe_token_expr(expr)
|
||||
if self.current.type is TOKEN_EOF:
|
||||
raise TemplateSyntaxError('unexpected end of template, '
|
||||
'expected %r.' % expr,
|
||||
self.current.lineno,
|
||||
self.name, self.filename)
|
||||
raise TemplateSyntaxError("expected token %r, got %r" %
|
||||
(expr, describe_token(self.current)),
|
||||
self.current.lineno,
|
||||
self.name, self.filename)
|
||||
try:
|
||||
return self.current
|
||||
finally:
|
||||
next(self)
|
||||
|
||||
|
||||
def get_lexer(environment):
|
||||
"""Return a lexer which is probably cached."""
|
||||
key = (environment.block_start_string,
|
||||
environment.block_end_string,
|
||||
environment.variable_start_string,
|
||||
environment.variable_end_string,
|
||||
environment.comment_start_string,
|
||||
environment.comment_end_string,
|
||||
environment.line_statement_prefix,
|
||||
environment.line_comment_prefix,
|
||||
environment.trim_blocks,
|
||||
environment.lstrip_blocks,
|
||||
environment.newline_sequence,
|
||||
environment.keep_trailing_newline)
|
||||
lexer = _lexer_cache.get(key)
|
||||
if lexer is None:
|
||||
lexer = Lexer(environment)
|
||||
_lexer_cache[key] = lexer
|
||||
return lexer
|
||||
|
||||
|
||||
class Lexer(object):
|
||||
"""Class that implements a lexer for a given environment. Automatically
|
||||
created by the environment class, usually you don't have to do that.
|
||||
|
||||
Note that the lexer is not automatically bound to an environment.
|
||||
Multiple environments can share the same lexer.
|
||||
"""
|
||||
|
||||
def __init__(self, environment):
|
||||
# shortcuts
|
||||
c = lambda x: re.compile(x, re.M | re.S)
|
||||
e = re.escape
|
||||
|
||||
# lexing rules for tags
|
||||
tag_rules = [
|
||||
(whitespace_re, TOKEN_WHITESPACE, None),
|
||||
(float_re, TOKEN_FLOAT, None),
|
||||
(integer_re, TOKEN_INTEGER, None),
|
||||
(name_re, TOKEN_NAME, None),
|
||||
(string_re, TOKEN_STRING, None),
|
||||
(operator_re, TOKEN_OPERATOR, None)
|
||||
]
|
||||
|
||||
# assemble the root lexing rule. because "|" is ungreedy
|
||||
# we have to sort by length so that the lexer continues working
|
||||
# as expected when we have parsing rules like <% for block and
|
||||
# <%= for variables. (if someone wants asp like syntax)
|
||||
# variables are just part of the rules if variable processing
|
||||
# is required.
|
||||
root_tag_rules = compile_rules(environment)
|
||||
|
||||
# block suffix if trimming is enabled
|
||||
block_suffix_re = environment.trim_blocks and '\\n?' or ''
|
||||
|
||||
# strip leading spaces if lstrip_blocks is enabled
|
||||
prefix_re = {}
|
||||
if environment.lstrip_blocks:
|
||||
# use '{%+' to manually disable lstrip_blocks behavior
|
||||
no_lstrip_re = e('+')
|
||||
# detect overlap between block and variable or comment strings
|
||||
block_diff = c(r'^%s(.*)' % e(environment.block_start_string))
|
||||
# make sure we don't mistake a block for a variable or a comment
|
||||
m = block_diff.match(environment.comment_start_string)
|
||||
no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
||||
m = block_diff.match(environment.variable_start_string)
|
||||
no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
||||
|
||||
# detect overlap between comment and variable strings
|
||||
comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string))
|
||||
m = comment_diff.match(environment.variable_start_string)
|
||||
no_variable_re = m and r'(?!%s)' % e(m.group(1)) or ''
|
||||
|
||||
lstrip_re = r'^[ \t]*'
|
||||
block_prefix_re = r'%s%s(?!%s)|%s\+?' % (
|
||||
lstrip_re,
|
||||
e(environment.block_start_string),
|
||||
no_lstrip_re,
|
||||
e(environment.block_start_string),
|
||||
)
|
||||
comment_prefix_re = r'%s%s%s|%s\+?' % (
|
||||
lstrip_re,
|
||||
e(environment.comment_start_string),
|
||||
no_variable_re,
|
||||
e(environment.comment_start_string),
|
||||
)
|
||||
prefix_re['block'] = block_prefix_re
|
||||
prefix_re['comment'] = comment_prefix_re
|
||||
else:
|
||||
block_prefix_re = '%s' % e(environment.block_start_string)
|
||||
|
||||
self.newline_sequence = environment.newline_sequence
|
||||
self.keep_trailing_newline = environment.keep_trailing_newline
|
||||
|
||||
# global lexing rules
|
||||
self.rules = {
|
||||
'root': [
|
||||
# directives
|
||||
(c('(.*?)(?:%s)' % '|'.join(
|
||||
[r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
|
||||
e(environment.block_start_string),
|
||||
block_prefix_re,
|
||||
e(environment.block_end_string),
|
||||
e(environment.block_end_string)
|
||||
)] + [
|
||||
r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r))
|
||||
for n, r in root_tag_rules
|
||||
])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
|
||||
# data
|
||||
(c('.+'), TOKEN_DATA, None)
|
||||
],
|
||||
# comments
|
||||
TOKEN_COMMENT_BEGIN: [
|
||||
(c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
|
||||
e(environment.comment_end_string),
|
||||
e(environment.comment_end_string),
|
||||
block_suffix_re
|
||||
)), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
|
||||
(c('(.)'), (Failure('Missing end of comment tag'),), None)
|
||||
],
|
||||
# blocks
|
||||
TOKEN_BLOCK_BEGIN: [
|
||||
(c('(?:\-%s\s*|%s)%s' % (
|
||||
e(environment.block_end_string),
|
||||
e(environment.block_end_string),
|
||||
block_suffix_re
|
||||
)), TOKEN_BLOCK_END, '#pop'),
|
||||
] + tag_rules,
|
||||
# variables
|
||||
TOKEN_VARIABLE_BEGIN: [
|
||||
(c('\-%s\s*|%s' % (
|
||||
e(environment.variable_end_string),
|
||||
e(environment.variable_end_string)
|
||||
)), TOKEN_VARIABLE_END, '#pop')
|
||||
] + tag_rules,
|
||||
# raw block
|
||||
TOKEN_RAW_BEGIN: [
|
||||
(c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
|
||||
e(environment.block_start_string),
|
||||
block_prefix_re,
|
||||
e(environment.block_end_string),
|
||||
e(environment.block_end_string),
|
||||
block_suffix_re
|
||||
)), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
|
||||
(c('(.)'), (Failure('Missing end of raw directive'),), None)
|
||||
],
|
||||
# line statements
|
||||
TOKEN_LINESTATEMENT_BEGIN: [
|
||||
(c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
|
||||
] + tag_rules,
|
||||
# line comments
|
||||
TOKEN_LINECOMMENT_BEGIN: [
|
||||
(c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
|
||||
TOKEN_LINECOMMENT_END), '#pop')
|
||||
]
|
||||
}
|
||||
|
||||
def _normalize_newlines(self, value):
|
||||
"""Called for strings and template data to normalize it to unicode."""
|
||||
return newline_re.sub(self.newline_sequence, value)
|
||||
|
||||
def tokenize(self, source, name=None, filename=None, state=None):
|
||||
"""Calls tokeniter + tokenize and wraps it in a token stream.
|
||||
"""
|
||||
stream = self.tokeniter(source, name, filename, state)
|
||||
return TokenStream(self.wrap(stream, name, filename), name, filename)
|
||||
|
||||
def wrap(self, stream, name=None, filename=None):
|
||||
"""This is called with the stream as returned by `tokenize` and wraps
|
||||
every token in a :class:`Token` and converts the value.
|
||||
"""
|
||||
for lineno, token, value in stream:
|
||||
if token in ignored_tokens:
|
||||
continue
|
||||
elif token == 'linestatement_begin':
|
||||
token = 'block_begin'
|
||||
elif token == 'linestatement_end':
|
||||
token = 'block_end'
|
||||
# we are not interested in those tokens in the parser
|
||||
elif token in ('raw_begin', 'raw_end'):
|
||||
continue
|
||||
elif token == 'data':
|
||||
value = self._normalize_newlines(value)
|
||||
elif token == 'keyword':
|
||||
token = value
|
||||
elif token == 'name':
|
||||
value = str(value)
|
||||
elif token == 'string':
|
||||
# try to unescape string
|
||||
try:
|
||||
value = self._normalize_newlines(value[1:-1]) \
|
||||
.encode('ascii', 'backslashreplace') \
|
||||
.decode('unicode-escape')
|
||||
except Exception as e:
|
||||
msg = str(e).split(':')[-1].strip()
|
||||
raise TemplateSyntaxError(msg, lineno, name, filename)
|
||||
# if we can express it as bytestring (ascii only)
|
||||
# we do that for support of semi broken APIs
|
||||
# as datetime.datetime.strftime. On python 3 this
|
||||
# call becomes a noop thanks to 2to3
|
||||
try:
|
||||
value = str(value)
|
||||
except UnicodeError:
|
||||
pass
|
||||
elif token == 'integer':
|
||||
value = int(value)
|
||||
elif token == 'float':
|
||||
value = float(value)
|
||||
elif token == 'operator':
|
||||
token = operators[value]
|
||||
yield Token(lineno, token, value)
|
||||
|
||||
def tokeniter(self, source, name, filename=None, state=None):
|
||||
"""This method tokenizes the text and returns the tokens in a
|
||||
generator. Use this method if you just want to tokenize a template.
|
||||
"""
|
||||
source = text_type(source)
|
||||
lines = source.splitlines()
|
||||
if self.keep_trailing_newline and source:
|
||||
for newline in ('\r\n', '\r', '\n'):
|
||||
if source.endswith(newline):
|
||||
lines.append('')
|
||||
break
|
||||
source = '\n'.join(lines)
|
||||
pos = 0
|
||||
lineno = 1
|
||||
stack = ['root']
|
||||
if state is not None and state != 'root':
|
||||
assert state in ('variable', 'block'), 'invalid state'
|
||||
stack.append(state + '_begin')
|
||||
else:
|
||||
state = 'root'
|
||||
statetokens = self.rules[stack[-1]]
|
||||
source_length = len(source)
|
||||
|
||||
balancing_stack = []
|
||||
|
||||
while 1:
|
||||
# tokenizer loop
|
||||
for regex, tokens, new_state in statetokens:
|
||||
m = regex.match(source, pos)
|
||||
# if no match we try again with the next rule
|
||||
if m is None:
|
||||
continue
|
||||
|
||||
# we only match blocks and variables if braces / parentheses
|
||||
# are balanced. continue parsing with the lower rule which
|
||||
# is the operator rule. do this only if the end tags look
|
||||
# like operators
|
||||
if balancing_stack and \
|
||||
tokens in ('variable_end', 'block_end',
|
||||
'linestatement_end'):
|
||||
continue
|
||||
|
||||
# tuples support more options
|
||||
if isinstance(tokens, tuple):
|
||||
for idx, token in enumerate(tokens):
|
||||
# failure group
|
||||
if token.__class__ is Failure:
|
||||
raise token(lineno, filename)
|
||||
# bygroup is a bit more complex, in that case we
|
||||
# yield for the current token the first named
|
||||
# group that matched
|
||||
elif token == '#bygroup':
|
||||
for key, value in iteritems(m.groupdict()):
|
||||
if value is not None:
|
||||
yield lineno, key, value
|
||||
lineno += value.count('\n')
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('%r wanted to resolve '
|
||||
'the token dynamically'
|
||||
' but no group matched'
|
||||
% regex)
|
||||
# normal group
|
||||
else:
|
||||
data = m.group(idx + 1)
|
||||
if data or token not in ignore_if_empty:
|
||||
yield lineno, token, data
|
||||
lineno += data.count('\n')
|
||||
|
||||
# strings as token just are yielded as it.
|
||||
else:
|
||||
data = m.group()
|
||||
# update brace/parentheses balance
|
||||
if tokens == 'operator':
|
||||
if data == '{':
|
||||
balancing_stack.append('}')
|
||||
elif data == '(':
|
||||
balancing_stack.append(')')
|
||||
elif data == '[':
|
||||
balancing_stack.append(']')
|
||||
elif data in ('}', ')', ']'):
|
||||
if not balancing_stack:
|
||||
raise TemplateSyntaxError('unexpected \'%s\'' %
|
||||
data, lineno, name,
|
||||
filename)
|
||||
expected_op = balancing_stack.pop()
|
||||
if expected_op != data:
|
||||
raise TemplateSyntaxError('unexpected \'%s\', '
|
||||
'expected \'%s\'' %
|
||||
(data, expected_op),
|
||||
lineno, name,
|
||||
filename)
|
||||
# yield items
|
||||
if data or tokens not in ignore_if_empty:
|
||||
yield lineno, tokens, data
|
||||
lineno += data.count('\n')
|
||||
|
||||
# fetch new position into new variable so that we can check
|
||||
# if there is a internal parsing error which would result
|
||||
# in an infinite loop
|
||||
pos2 = m.end()
|
||||
|
||||
# handle state changes
|
||||
if new_state is not None:
|
||||
# remove the uppermost state
|
||||
if new_state == '#pop':
|
||||
stack.pop()
|
||||
# resolve the new state by group checking
|
||||
elif new_state == '#bygroup':
|
||||
for key, value in iteritems(m.groupdict()):
|
||||
if value is not None:
|
||||
stack.append(key)
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('%r wanted to resolve the '
|
||||
'new state dynamically but'
|
||||
' no group matched' %
|
||||
regex)
|
||||
# direct state name given
|
||||
else:
|
||||
stack.append(new_state)
|
||||
statetokens = self.rules[stack[-1]]
|
||||
# we are still at the same position and no stack change.
|
||||
# this means a loop without break condition, avoid that and
|
||||
# raise error
|
||||
elif pos2 == pos:
|
||||
raise RuntimeError('%r yielded empty string without '
|
||||
'stack change' % regex)
|
||||
# publish new function and start again
|
||||
pos = pos2
|
||||
break
|
||||
# if loop terminated without break we haven't found a single match
|
||||
# either we are at the end of the file or we have a problem
|
||||
else:
|
||||
# end of text
|
||||
if pos >= source_length:
|
||||
return
|
||||
# something went wrong
|
||||
raise TemplateSyntaxError('unexpected char %r at %d' %
|
||||
(source[pos], pos), lineno,
|
||||
name, filename)
|
||||
Vendored
-471
@@ -1,471 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.loaders
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Jinja loader classes.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import weakref
|
||||
from types import ModuleType
|
||||
from os import path
|
||||
from hashlib import sha1
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from jinja2.utils import open_if_exists, internalcode
|
||||
from jinja2._compat import string_types, iteritems
|
||||
|
||||
|
||||
def split_template_path(template):
|
||||
"""Split a path into segments and perform a sanity check. If it detects
|
||||
'..' in the path it will raise a `TemplateNotFound` error.
|
||||
"""
|
||||
pieces = []
|
||||
for piece in template.split('/'):
|
||||
if path.sep in piece \
|
||||
or (path.altsep and path.altsep in piece) or \
|
||||
piece == path.pardir:
|
||||
raise TemplateNotFound(template)
|
||||
elif piece and piece != '.':
|
||||
pieces.append(piece)
|
||||
return pieces
|
||||
|
||||
|
||||
class BaseLoader(object):
|
||||
"""Baseclass for all loaders. Subclass this and override `get_source` to
|
||||
implement a custom loading mechanism. The environment provides a
|
||||
`get_template` method that calls the loader's `load` method to get the
|
||||
:class:`Template` object.
|
||||
|
||||
A very basic example for a loader that looks up templates on the file
|
||||
system could look like this::
|
||||
|
||||
from jinja2 import BaseLoader, TemplateNotFound
|
||||
from os.path import join, exists, getmtime
|
||||
|
||||
class MyLoader(BaseLoader):
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def get_source(self, environment, template):
|
||||
path = join(self.path, template)
|
||||
if not exists(path):
|
||||
raise TemplateNotFound(template)
|
||||
mtime = getmtime(path)
|
||||
with file(path) as f:
|
||||
source = f.read().decode('utf-8')
|
||||
return source, path, lambda: mtime == getmtime(path)
|
||||
"""
|
||||
|
||||
#: if set to `False` it indicates that the loader cannot provide access
|
||||
#: to the source of templates.
|
||||
#:
|
||||
#: .. versionadded:: 2.4
|
||||
has_source_access = True
|
||||
|
||||
def get_source(self, environment, template):
|
||||
"""Get the template source, filename and reload helper for a template.
|
||||
It's passed the environment and template name and has to return a
|
||||
tuple in the form ``(source, filename, uptodate)`` or raise a
|
||||
`TemplateNotFound` error if it can't locate the template.
|
||||
|
||||
The source part of the returned tuple must be the source of the
|
||||
template as unicode string or a ASCII bytestring. The filename should
|
||||
be the name of the file on the filesystem if it was loaded from there,
|
||||
otherwise `None`. The filename is used by python for the tracebacks
|
||||
if no loader extension is used.
|
||||
|
||||
The last item in the tuple is the `uptodate` function. If auto
|
||||
reloading is enabled it's always called to check if the template
|
||||
changed. No arguments are passed so the function must store the
|
||||
old state somewhere (for example in a closure). If it returns `False`
|
||||
the template will be reloaded.
|
||||
"""
|
||||
if not self.has_source_access:
|
||||
raise RuntimeError('%s cannot provide access to the source' %
|
||||
self.__class__.__name__)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self):
|
||||
"""Iterates over all templates. If the loader does not support that
|
||||
it should raise a :exc:`TypeError` which is the default behavior.
|
||||
"""
|
||||
raise TypeError('this loader cannot iterate over all templates')
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
"""Loads a template. This method looks up the template in the cache
|
||||
or loads one by calling :meth:`get_source`. Subclasses should not
|
||||
override this method as loaders working on collections of other
|
||||
loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
|
||||
will not call this method but `get_source` directly.
|
||||
"""
|
||||
code = None
|
||||
if globals is None:
|
||||
globals = {}
|
||||
|
||||
# first we try to get the source for this template together
|
||||
# with the filename and the uptodate function.
|
||||
source, filename, uptodate = self.get_source(environment, name)
|
||||
|
||||
# try to load the code from the bytecode cache if there is a
|
||||
# bytecode cache configured.
|
||||
bcc = environment.bytecode_cache
|
||||
if bcc is not None:
|
||||
bucket = bcc.get_bucket(environment, name, filename, source)
|
||||
code = bucket.code
|
||||
|
||||
# if we don't have code so far (not cached, no longer up to
|
||||
# date) etc. we compile the template
|
||||
if code is None:
|
||||
code = environment.compile(source, name, filename)
|
||||
|
||||
# if the bytecode cache is available and the bucket doesn't
|
||||
# have a code so far, we give the bucket the new code and put
|
||||
# it back to the bytecode cache.
|
||||
if bcc is not None and bucket.code is None:
|
||||
bucket.code = code
|
||||
bcc.set_bucket(bucket)
|
||||
|
||||
return environment.template_class.from_code(environment, code,
|
||||
globals, uptodate)
|
||||
|
||||
|
||||
class FileSystemLoader(BaseLoader):
|
||||
"""Loads templates from the file system. This loader can find templates
|
||||
in folders on the file system and is the preferred way to load them.
|
||||
|
||||
The loader takes the path to the templates as string, or if multiple
|
||||
locations are wanted a list of them which is then looked up in the
|
||||
given order:
|
||||
|
||||
>>> loader = FileSystemLoader('/path/to/templates')
|
||||
>>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
|
||||
|
||||
Per default the template encoding is ``'utf-8'`` which can be changed
|
||||
by setting the `encoding` parameter to something else.
|
||||
"""
|
||||
|
||||
def __init__(self, searchpath, encoding='utf-8'):
|
||||
if isinstance(searchpath, string_types):
|
||||
searchpath = [searchpath]
|
||||
self.searchpath = list(searchpath)
|
||||
self.encoding = encoding
|
||||
|
||||
def get_source(self, environment, template):
|
||||
pieces = split_template_path(template)
|
||||
for searchpath in self.searchpath:
|
||||
filename = path.join(searchpath, *pieces)
|
||||
f = open_if_exists(filename)
|
||||
if f is None:
|
||||
continue
|
||||
try:
|
||||
contents = f.read().decode(self.encoding)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
mtime = path.getmtime(filename)
|
||||
def uptodate():
|
||||
try:
|
||||
return path.getmtime(filename) == mtime
|
||||
except OSError:
|
||||
return False
|
||||
return contents, filename, uptodate
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self):
|
||||
found = set()
|
||||
for searchpath in self.searchpath:
|
||||
for dirpath, dirnames, filenames in os.walk(searchpath):
|
||||
for filename in filenames:
|
||||
template = os.path.join(dirpath, filename) \
|
||||
[len(searchpath):].strip(os.path.sep) \
|
||||
.replace(os.path.sep, '/')
|
||||
if template[:2] == './':
|
||||
template = template[2:]
|
||||
if template not in found:
|
||||
found.add(template)
|
||||
return sorted(found)
|
||||
|
||||
|
||||
class PackageLoader(BaseLoader):
|
||||
"""Load templates from python eggs or packages. It is constructed with
|
||||
the name of the python package and the path to the templates in that
|
||||
package::
|
||||
|
||||
loader = PackageLoader('mypackage', 'views')
|
||||
|
||||
If the package path is not given, ``'templates'`` is assumed.
|
||||
|
||||
Per default the template encoding is ``'utf-8'`` which can be changed
|
||||
by setting the `encoding` parameter to something else. Due to the nature
|
||||
of eggs it's only possible to reload templates if the package was loaded
|
||||
from the file system and not a zip file.
|
||||
"""
|
||||
|
||||
def __init__(self, package_name, package_path='templates',
|
||||
encoding='utf-8'):
|
||||
from pkg_resources import DefaultProvider, ResourceManager, \
|
||||
get_provider
|
||||
provider = get_provider(package_name)
|
||||
self.encoding = encoding
|
||||
self.manager = ResourceManager()
|
||||
self.filesystem_bound = isinstance(provider, DefaultProvider)
|
||||
self.provider = provider
|
||||
self.package_path = package_path
|
||||
|
||||
def get_source(self, environment, template):
|
||||
pieces = split_template_path(template)
|
||||
p = '/'.join((self.package_path,) + tuple(pieces))
|
||||
if not self.provider.has_resource(p):
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
filename = uptodate = None
|
||||
if self.filesystem_bound:
|
||||
filename = self.provider.get_resource_filename(self.manager, p)
|
||||
mtime = path.getmtime(filename)
|
||||
def uptodate():
|
||||
try:
|
||||
return path.getmtime(filename) == mtime
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
source = self.provider.get_resource_string(self.manager, p)
|
||||
return source.decode(self.encoding), filename, uptodate
|
||||
|
||||
def list_templates(self):
|
||||
path = self.package_path
|
||||
if path[:2] == './':
|
||||
path = path[2:]
|
||||
elif path == '.':
|
||||
path = ''
|
||||
offset = len(path)
|
||||
results = []
|
||||
def _walk(path):
|
||||
for filename in self.provider.resource_listdir(path):
|
||||
fullname = path + '/' + filename
|
||||
if self.provider.resource_isdir(fullname):
|
||||
_walk(fullname)
|
||||
else:
|
||||
results.append(fullname[offset:].lstrip('/'))
|
||||
_walk(path)
|
||||
results.sort()
|
||||
return results
|
||||
|
||||
|
||||
class DictLoader(BaseLoader):
|
||||
"""Loads a template from a python dict. It's passed a dict of unicode
|
||||
strings bound to template names. This loader is useful for unittesting:
|
||||
|
||||
>>> loader = DictLoader({'index.html': 'source here'})
|
||||
|
||||
Because auto reloading is rarely useful this is disabled per default.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping):
|
||||
self.mapping = mapping
|
||||
|
||||
def get_source(self, environment, template):
|
||||
if template in self.mapping:
|
||||
source = self.mapping[template]
|
||||
return source, None, lambda: source == self.mapping.get(template)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def list_templates(self):
|
||||
return sorted(self.mapping)
|
||||
|
||||
|
||||
class FunctionLoader(BaseLoader):
|
||||
"""A loader that is passed a function which does the loading. The
|
||||
function becomes the name of the template passed and has to return either
|
||||
an unicode string with the template source, a tuple in the form ``(source,
|
||||
filename, uptodatefunc)`` or `None` if the template does not exist.
|
||||
|
||||
>>> def load_template(name):
|
||||
... if name == 'index.html':
|
||||
... return '...'
|
||||
...
|
||||
>>> loader = FunctionLoader(load_template)
|
||||
|
||||
The `uptodatefunc` is a function that is called if autoreload is enabled
|
||||
and has to return `True` if the template is still up to date. For more
|
||||
details have a look at :meth:`BaseLoader.get_source` which has the same
|
||||
return value.
|
||||
"""
|
||||
|
||||
def __init__(self, load_func):
|
||||
self.load_func = load_func
|
||||
|
||||
def get_source(self, environment, template):
|
||||
rv = self.load_func(template)
|
||||
if rv is None:
|
||||
raise TemplateNotFound(template)
|
||||
elif isinstance(rv, string_types):
|
||||
return rv, None, None
|
||||
return rv
|
||||
|
||||
|
||||
class PrefixLoader(BaseLoader):
|
||||
"""A loader that is passed a dict of loaders where each loader is bound
|
||||
to a prefix. The prefix is delimited from the template by a slash per
|
||||
default, which can be changed by setting the `delimiter` argument to
|
||||
something else::
|
||||
|
||||
loader = PrefixLoader({
|
||||
'app1': PackageLoader('mypackage.app1'),
|
||||
'app2': PackageLoader('mypackage.app2')
|
||||
})
|
||||
|
||||
By loading ``'app1/index.html'`` the file from the app1 package is loaded,
|
||||
by loading ``'app2/index.html'`` the file from the second.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping, delimiter='/'):
|
||||
self.mapping = mapping
|
||||
self.delimiter = delimiter
|
||||
|
||||
def get_loader(self, template):
|
||||
try:
|
||||
prefix, name = template.split(self.delimiter, 1)
|
||||
loader = self.mapping[prefix]
|
||||
except (ValueError, KeyError):
|
||||
raise TemplateNotFound(template)
|
||||
return loader, name
|
||||
|
||||
def get_source(self, environment, template):
|
||||
loader, name = self.get_loader(template)
|
||||
try:
|
||||
return loader.get_source(environment, name)
|
||||
except TemplateNotFound:
|
||||
# re-raise the exception with the correct fileame here.
|
||||
# (the one that includes the prefix)
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
loader, local_name = self.get_loader(name)
|
||||
try:
|
||||
return loader.load(environment, local_name)
|
||||
except TemplateNotFound:
|
||||
# re-raise the exception with the correct fileame here.
|
||||
# (the one that includes the prefix)
|
||||
raise TemplateNotFound(name)
|
||||
|
||||
def list_templates(self):
|
||||
result = []
|
||||
for prefix, loader in iteritems(self.mapping):
|
||||
for template in loader.list_templates():
|
||||
result.append(prefix + self.delimiter + template)
|
||||
return result
|
||||
|
||||
|
||||
class ChoiceLoader(BaseLoader):
|
||||
"""This loader works like the `PrefixLoader` just that no prefix is
|
||||
specified. If a template could not be found by one loader the next one
|
||||
is tried.
|
||||
|
||||
>>> loader = ChoiceLoader([
|
||||
... FileSystemLoader('/path/to/user/templates'),
|
||||
... FileSystemLoader('/path/to/system/templates')
|
||||
... ])
|
||||
|
||||
This is useful if you want to allow users to override builtin templates
|
||||
from a different location.
|
||||
"""
|
||||
|
||||
def __init__(self, loaders):
|
||||
self.loaders = loaders
|
||||
|
||||
def get_source(self, environment, template):
|
||||
for loader in self.loaders:
|
||||
try:
|
||||
return loader.get_source(environment, template)
|
||||
except TemplateNotFound:
|
||||
pass
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
for loader in self.loaders:
|
||||
try:
|
||||
return loader.load(environment, name, globals)
|
||||
except TemplateNotFound:
|
||||
pass
|
||||
raise TemplateNotFound(name)
|
||||
|
||||
def list_templates(self):
|
||||
found = set()
|
||||
for loader in self.loaders:
|
||||
found.update(loader.list_templates())
|
||||
return sorted(found)
|
||||
|
||||
|
||||
class _TemplateModule(ModuleType):
|
||||
"""Like a normal module but with support for weak references"""
|
||||
|
||||
|
||||
class ModuleLoader(BaseLoader):
|
||||
"""This loader loads templates from precompiled templates.
|
||||
|
||||
Example usage:
|
||||
|
||||
>>> loader = ChoiceLoader([
|
||||
... ModuleLoader('/path/to/compiled/templates'),
|
||||
... FileSystemLoader('/path/to/templates')
|
||||
... ])
|
||||
|
||||
Templates can be precompiled with :meth:`Environment.compile_templates`.
|
||||
"""
|
||||
|
||||
has_source_access = False
|
||||
|
||||
def __init__(self, path):
|
||||
package_name = '_jinja2_module_templates_%x' % id(self)
|
||||
|
||||
# create a fake module that looks for the templates in the
|
||||
# path given.
|
||||
mod = _TemplateModule(package_name)
|
||||
if isinstance(path, string_types):
|
||||
path = [path]
|
||||
else:
|
||||
path = list(path)
|
||||
mod.__path__ = path
|
||||
|
||||
sys.modules[package_name] = weakref.proxy(mod,
|
||||
lambda x: sys.modules.pop(package_name, None))
|
||||
|
||||
# the only strong reference, the sys.modules entry is weak
|
||||
# so that the garbage collector can remove it once the
|
||||
# loader that created it goes out of business.
|
||||
self.module = mod
|
||||
self.package_name = package_name
|
||||
|
||||
@staticmethod
|
||||
def get_template_key(name):
|
||||
return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
|
||||
|
||||
@staticmethod
|
||||
def get_module_filename(name):
|
||||
return ModuleLoader.get_template_key(name) + '.py'
|
||||
|
||||
@internalcode
|
||||
def load(self, environment, name, globals=None):
|
||||
key = self.get_template_key(name)
|
||||
module = '%s.%s' % (self.package_name, key)
|
||||
mod = getattr(self.module, module, None)
|
||||
if mod is None:
|
||||
try:
|
||||
mod = __import__(module, None, None, ['root'])
|
||||
except ImportError:
|
||||
raise TemplateNotFound(name)
|
||||
|
||||
# remove the entry from sys.modules, we only want the attribute
|
||||
# on the module object we have stored on the loader.
|
||||
sys.modules.pop(module, None)
|
||||
|
||||
return environment.template_class.from_module_dict(
|
||||
environment, mod.__dict__, globals)
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe
|
||||
~~~~~~~~~~
|
||||
|
||||
Implements a Markup string.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
from ._compat import text_type, string_types, int_types, \
|
||||
unichr, PY2
|
||||
|
||||
|
||||
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
||||
|
||||
|
||||
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
||||
_entity_re = re.compile(r'&([^;]+);')
|
||||
|
||||
|
||||
class Markup(text_type):
|
||||
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
||||
needing to be escaped. This implements the `__html__` interface a couple
|
||||
of frameworks and web applications use. :class:`Markup` is a direct
|
||||
subclass of `unicode` and provides all the methods of `unicode` just that
|
||||
it escapes arguments passed and always returns `Markup`.
|
||||
|
||||
The `escape` function returns markup objects so that double escaping can't
|
||||
happen.
|
||||
|
||||
The constructor of the :class:`Markup` class can be used for three
|
||||
different things: When passed an unicode object it's assumed to be safe,
|
||||
when passed an object with an HTML representation (has an `__html__`
|
||||
method) that representation is used, otherwise the object passed is
|
||||
converted into a unicode string and then assumed to be safe:
|
||||
|
||||
>>> Markup("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<a href="#">foo</a>'
|
||||
...
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<a href="#">foo</a>')
|
||||
|
||||
If you want object passed being always treated as unsafe you can use the
|
||||
:meth:`escape` classmethod to create a :class:`Markup` object:
|
||||
|
||||
>>> Markup.escape("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
|
||||
Operations on a markup string are markup aware which means that all
|
||||
arguments are passed through the :func:`escape` function:
|
||||
|
||||
>>> em = Markup("<em>%s</em>")
|
||||
>>> em % "foo & bar"
|
||||
Markup(u'<em>foo & bar</em>')
|
||||
>>> strong = Markup("<strong>%(text)s</strong>")
|
||||
>>> strong % {'text': '<blink>hacker here</blink>'}
|
||||
Markup(u'<strong><blink>hacker here</blink></strong>')
|
||||
>>> Markup("<em>Hello</em> ") + "<foo>"
|
||||
Markup(u'<em>Hello</em> <foo>')
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, base=u'', encoding=None, errors='strict'):
|
||||
if hasattr(base, '__html__'):
|
||||
base = base.__html__()
|
||||
if encoding is None:
|
||||
return text_type.__new__(cls, base)
|
||||
return text_type.__new__(cls, base, encoding, errors)
|
||||
|
||||
def __html__(self):
|
||||
return self
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, string_types) or hasattr(other, '__html__'):
|
||||
return self.__class__(super(Markup, self).__add__(self.escape(other)))
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other):
|
||||
if hasattr(other, '__html__') or isinstance(other, string_types):
|
||||
return self.escape(other).__add__(self)
|
||||
return NotImplemented
|
||||
|
||||
def __mul__(self, num):
|
||||
if isinstance(num, int_types):
|
||||
return self.__class__(text_type.__mul__(self, num))
|
||||
return NotImplemented
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __mod__(self, arg):
|
||||
if isinstance(arg, tuple):
|
||||
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
||||
else:
|
||||
arg = _MarkupEscapeHelper(arg, self.escape)
|
||||
return self.__class__(text_type.__mod__(self, arg))
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (
|
||||
self.__class__.__name__,
|
||||
text_type.__repr__(self)
|
||||
)
|
||||
|
||||
def join(self, seq):
|
||||
return self.__class__(text_type.join(self, map(self.escape, seq)))
|
||||
join.__doc__ = text_type.join.__doc__
|
||||
|
||||
def split(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
|
||||
split.__doc__ = text_type.split.__doc__
|
||||
|
||||
def rsplit(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
|
||||
rsplit.__doc__ = text_type.rsplit.__doc__
|
||||
|
||||
def splitlines(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs)))
|
||||
splitlines.__doc__ = text_type.splitlines.__doc__
|
||||
|
||||
def unescape(self):
|
||||
r"""Unescape markup again into an text_type string. This also resolves
|
||||
known HTML4 and XHTML entities:
|
||||
|
||||
>>> Markup("Main » <em>About</em>").unescape()
|
||||
u'Main \xbb <em>About</em>'
|
||||
"""
|
||||
from _constants import HTML_ENTITIES
|
||||
def handle_match(m):
|
||||
name = m.group(1)
|
||||
if name in HTML_ENTITIES:
|
||||
return unichr(HTML_ENTITIES[name])
|
||||
try:
|
||||
if name[:2] in ('#x', '#X'):
|
||||
return unichr(int(name[2:], 16))
|
||||
elif name.startswith('#'):
|
||||
return unichr(int(name[1:]))
|
||||
except ValueError:
|
||||
pass
|
||||
return u''
|
||||
return _entity_re.sub(handle_match, text_type(self))
|
||||
|
||||
def striptags(self):
|
||||
r"""Unescape markup into an text_type string and strip all tags. This
|
||||
also resolves known HTML4 and XHTML entities. Whitespace is
|
||||
normalized to one:
|
||||
|
||||
>>> Markup("Main » <em>About</em>").striptags()
|
||||
u'Main \xbb About'
|
||||
"""
|
||||
stripped = u' '.join(_striptags_re.sub('', self).split())
|
||||
return Markup(stripped).unescape()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
"""Escape the string. Works like :func:`escape` with the difference
|
||||
that for subclasses of :class:`Markup` this function would return the
|
||||
correct subclass.
|
||||
"""
|
||||
rv = escape(s)
|
||||
if rv.__class__ is not cls:
|
||||
return cls(rv)
|
||||
return rv
|
||||
|
||||
def make_wrapper(name):
|
||||
orig = getattr(text_type, name)
|
||||
def func(self, *args, **kwargs):
|
||||
args = _escape_argspec(list(args), enumerate(args), self.escape)
|
||||
#_escape_argspec(kwargs, kwargs.iteritems(), None)
|
||||
return self.__class__(orig(self, *args, **kwargs))
|
||||
func.__name__ = orig.__name__
|
||||
func.__doc__ = orig.__doc__
|
||||
return func
|
||||
|
||||
for method in '__getitem__', 'capitalize', \
|
||||
'title', 'lower', 'upper', 'replace', 'ljust', \
|
||||
'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
|
||||
'translate', 'expandtabs', 'swapcase', 'zfill':
|
||||
locals()[method] = make_wrapper(method)
|
||||
|
||||
# new in python 2.5
|
||||
if hasattr(text_type, 'partition'):
|
||||
def partition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.partition(self, self.escape(sep))))
|
||||
def rpartition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.rpartition(self, self.escape(sep))))
|
||||
|
||||
# new in python 2.6
|
||||
if hasattr(text_type, 'format'):
|
||||
format = make_wrapper('format')
|
||||
|
||||
# not in python 3
|
||||
if hasattr(text_type, '__getslice__'):
|
||||
__getslice__ = make_wrapper('__getslice__')
|
||||
|
||||
del method, make_wrapper
|
||||
|
||||
|
||||
def _escape_argspec(obj, iterable, escape):
|
||||
"""Helper for various string-wrapped functions."""
|
||||
for key, value in iterable:
|
||||
if hasattr(value, '__html__') or isinstance(value, string_types):
|
||||
obj[key] = escape(value)
|
||||
return obj
|
||||
|
||||
|
||||
class _MarkupEscapeHelper(object):
|
||||
"""Helper for Markup.__mod__"""
|
||||
|
||||
def __init__(self, obj, escape):
|
||||
self.obj = obj
|
||||
self.escape = escape
|
||||
|
||||
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
|
||||
__unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
|
||||
__repr__ = lambda s: str(s.escape(repr(s.obj)))
|
||||
__int__ = lambda s: int(s.obj)
|
||||
__float__ = lambda s: float(s.obj)
|
||||
|
||||
|
||||
# we have to import it down here as the speedups and native
|
||||
# modules imports the markup type which is define above.
|
||||
try:
|
||||
from _speedups import escape, escape_silent, soft_unicode
|
||||
except ImportError:
|
||||
from ._native import escape, escape_silent, soft_unicode
|
||||
|
||||
if not PY2:
|
||||
soft_str = soft_unicode
|
||||
__all__.append('soft_str')
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._compat
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Compatibility module for different Python versions.
|
||||
|
||||
:copyright: (c) 2013 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
if not PY2:
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
unichr = chr
|
||||
int_types = (int,)
|
||||
else:
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
unichr = unichr
|
||||
int_types = (int, long)
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._constants
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Highlevel implementation of the Markup string.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
HTML_ENTITIES = {
|
||||
'AElig': 198,
|
||||
'Aacute': 193,
|
||||
'Acirc': 194,
|
||||
'Agrave': 192,
|
||||
'Alpha': 913,
|
||||
'Aring': 197,
|
||||
'Atilde': 195,
|
||||
'Auml': 196,
|
||||
'Beta': 914,
|
||||
'Ccedil': 199,
|
||||
'Chi': 935,
|
||||
'Dagger': 8225,
|
||||
'Delta': 916,
|
||||
'ETH': 208,
|
||||
'Eacute': 201,
|
||||
'Ecirc': 202,
|
||||
'Egrave': 200,
|
||||
'Epsilon': 917,
|
||||
'Eta': 919,
|
||||
'Euml': 203,
|
||||
'Gamma': 915,
|
||||
'Iacute': 205,
|
||||
'Icirc': 206,
|
||||
'Igrave': 204,
|
||||
'Iota': 921,
|
||||
'Iuml': 207,
|
||||
'Kappa': 922,
|
||||
'Lambda': 923,
|
||||
'Mu': 924,
|
||||
'Ntilde': 209,
|
||||
'Nu': 925,
|
||||
'OElig': 338,
|
||||
'Oacute': 211,
|
||||
'Ocirc': 212,
|
||||
'Ograve': 210,
|
||||
'Omega': 937,
|
||||
'Omicron': 927,
|
||||
'Oslash': 216,
|
||||
'Otilde': 213,
|
||||
'Ouml': 214,
|
||||
'Phi': 934,
|
||||
'Pi': 928,
|
||||
'Prime': 8243,
|
||||
'Psi': 936,
|
||||
'Rho': 929,
|
||||
'Scaron': 352,
|
||||
'Sigma': 931,
|
||||
'THORN': 222,
|
||||
'Tau': 932,
|
||||
'Theta': 920,
|
||||
'Uacute': 218,
|
||||
'Ucirc': 219,
|
||||
'Ugrave': 217,
|
||||
'Upsilon': 933,
|
||||
'Uuml': 220,
|
||||
'Xi': 926,
|
||||
'Yacute': 221,
|
||||
'Yuml': 376,
|
||||
'Zeta': 918,
|
||||
'aacute': 225,
|
||||
'acirc': 226,
|
||||
'acute': 180,
|
||||
'aelig': 230,
|
||||
'agrave': 224,
|
||||
'alefsym': 8501,
|
||||
'alpha': 945,
|
||||
'amp': 38,
|
||||
'and': 8743,
|
||||
'ang': 8736,
|
||||
'apos': 39,
|
||||
'aring': 229,
|
||||
'asymp': 8776,
|
||||
'atilde': 227,
|
||||
'auml': 228,
|
||||
'bdquo': 8222,
|
||||
'beta': 946,
|
||||
'brvbar': 166,
|
||||
'bull': 8226,
|
||||
'cap': 8745,
|
||||
'ccedil': 231,
|
||||
'cedil': 184,
|
||||
'cent': 162,
|
||||
'chi': 967,
|
||||
'circ': 710,
|
||||
'clubs': 9827,
|
||||
'cong': 8773,
|
||||
'copy': 169,
|
||||
'crarr': 8629,
|
||||
'cup': 8746,
|
||||
'curren': 164,
|
||||
'dArr': 8659,
|
||||
'dagger': 8224,
|
||||
'darr': 8595,
|
||||
'deg': 176,
|
||||
'delta': 948,
|
||||
'diams': 9830,
|
||||
'divide': 247,
|
||||
'eacute': 233,
|
||||
'ecirc': 234,
|
||||
'egrave': 232,
|
||||
'empty': 8709,
|
||||
'emsp': 8195,
|
||||
'ensp': 8194,
|
||||
'epsilon': 949,
|
||||
'equiv': 8801,
|
||||
'eta': 951,
|
||||
'eth': 240,
|
||||
'euml': 235,
|
||||
'euro': 8364,
|
||||
'exist': 8707,
|
||||
'fnof': 402,
|
||||
'forall': 8704,
|
||||
'frac12': 189,
|
||||
'frac14': 188,
|
||||
'frac34': 190,
|
||||
'frasl': 8260,
|
||||
'gamma': 947,
|
||||
'ge': 8805,
|
||||
'gt': 62,
|
||||
'hArr': 8660,
|
||||
'harr': 8596,
|
||||
'hearts': 9829,
|
||||
'hellip': 8230,
|
||||
'iacute': 237,
|
||||
'icirc': 238,
|
||||
'iexcl': 161,
|
||||
'igrave': 236,
|
||||
'image': 8465,
|
||||
'infin': 8734,
|
||||
'int': 8747,
|
||||
'iota': 953,
|
||||
'iquest': 191,
|
||||
'isin': 8712,
|
||||
'iuml': 239,
|
||||
'kappa': 954,
|
||||
'lArr': 8656,
|
||||
'lambda': 955,
|
||||
'lang': 9001,
|
||||
'laquo': 171,
|
||||
'larr': 8592,
|
||||
'lceil': 8968,
|
||||
'ldquo': 8220,
|
||||
'le': 8804,
|
||||
'lfloor': 8970,
|
||||
'lowast': 8727,
|
||||
'loz': 9674,
|
||||
'lrm': 8206,
|
||||
'lsaquo': 8249,
|
||||
'lsquo': 8216,
|
||||
'lt': 60,
|
||||
'macr': 175,
|
||||
'mdash': 8212,
|
||||
'micro': 181,
|
||||
'middot': 183,
|
||||
'minus': 8722,
|
||||
'mu': 956,
|
||||
'nabla': 8711,
|
||||
'nbsp': 160,
|
||||
'ndash': 8211,
|
||||
'ne': 8800,
|
||||
'ni': 8715,
|
||||
'not': 172,
|
||||
'notin': 8713,
|
||||
'nsub': 8836,
|
||||
'ntilde': 241,
|
||||
'nu': 957,
|
||||
'oacute': 243,
|
||||
'ocirc': 244,
|
||||
'oelig': 339,
|
||||
'ograve': 242,
|
||||
'oline': 8254,
|
||||
'omega': 969,
|
||||
'omicron': 959,
|
||||
'oplus': 8853,
|
||||
'or': 8744,
|
||||
'ordf': 170,
|
||||
'ordm': 186,
|
||||
'oslash': 248,
|
||||
'otilde': 245,
|
||||
'otimes': 8855,
|
||||
'ouml': 246,
|
||||
'para': 182,
|
||||
'part': 8706,
|
||||
'permil': 8240,
|
||||
'perp': 8869,
|
||||
'phi': 966,
|
||||
'pi': 960,
|
||||
'piv': 982,
|
||||
'plusmn': 177,
|
||||
'pound': 163,
|
||||
'prime': 8242,
|
||||
'prod': 8719,
|
||||
'prop': 8733,
|
||||
'psi': 968,
|
||||
'quot': 34,
|
||||
'rArr': 8658,
|
||||
'radic': 8730,
|
||||
'rang': 9002,
|
||||
'raquo': 187,
|
||||
'rarr': 8594,
|
||||
'rceil': 8969,
|
||||
'rdquo': 8221,
|
||||
'real': 8476,
|
||||
'reg': 174,
|
||||
'rfloor': 8971,
|
||||
'rho': 961,
|
||||
'rlm': 8207,
|
||||
'rsaquo': 8250,
|
||||
'rsquo': 8217,
|
||||
'sbquo': 8218,
|
||||
'scaron': 353,
|
||||
'sdot': 8901,
|
||||
'sect': 167,
|
||||
'shy': 173,
|
||||
'sigma': 963,
|
||||
'sigmaf': 962,
|
||||
'sim': 8764,
|
||||
'spades': 9824,
|
||||
'sub': 8834,
|
||||
'sube': 8838,
|
||||
'sum': 8721,
|
||||
'sup': 8835,
|
||||
'sup1': 185,
|
||||
'sup2': 178,
|
||||
'sup3': 179,
|
||||
'supe': 8839,
|
||||
'szlig': 223,
|
||||
'tau': 964,
|
||||
'there4': 8756,
|
||||
'theta': 952,
|
||||
'thetasym': 977,
|
||||
'thinsp': 8201,
|
||||
'thorn': 254,
|
||||
'tilde': 732,
|
||||
'times': 215,
|
||||
'trade': 8482,
|
||||
'uArr': 8657,
|
||||
'uacute': 250,
|
||||
'uarr': 8593,
|
||||
'ucirc': 251,
|
||||
'ugrave': 249,
|
||||
'uml': 168,
|
||||
'upsih': 978,
|
||||
'upsilon': 965,
|
||||
'uuml': 252,
|
||||
'weierp': 8472,
|
||||
'xi': 958,
|
||||
'yacute': 253,
|
||||
'yen': 165,
|
||||
'yuml': 255,
|
||||
'zeta': 950,
|
||||
'zwj': 8205,
|
||||
'zwnj': 8204
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._native
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Native Python implementation the C module is not compiled.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from ._compat import text_type
|
||||
|
||||
|
||||
def escape(s):
|
||||
"""Convert the characters &, <, >, ' and " in string s to HTML-safe
|
||||
sequences. Use this if you need to display text that might contain
|
||||
such characters in HTML. Marks return value as markup string.
|
||||
"""
|
||||
if hasattr(s, '__html__'):
|
||||
return s.__html__()
|
||||
return Markup(text_type(s)
|
||||
.replace('&', '&')
|
||||
.replace('>', '>')
|
||||
.replace('<', '<')
|
||||
.replace("'", ''')
|
||||
.replace('"', '"')
|
||||
)
|
||||
|
||||
|
||||
def escape_silent(s):
|
||||
"""Like :func:`escape` but converts `None` into an empty
|
||||
markup string.
|
||||
"""
|
||||
if s is None:
|
||||
return Markup()
|
||||
return escape(s)
|
||||
|
||||
|
||||
def soft_unicode(s):
|
||||
"""Make a string unicode if it isn't already. That way a markup
|
||||
string is not converted back to unicode.
|
||||
"""
|
||||
if not isinstance(s, text_type):
|
||||
s = text_type(s)
|
||||
return s
|
||||
Vendored
-103
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.meta
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module implements various functions that exposes information about
|
||||
templates that might be interesting for various kinds of applications.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.compiler import CodeGenerator
|
||||
from jinja2._compat import string_types
|
||||
|
||||
|
||||
class TrackingCodeGenerator(CodeGenerator):
|
||||
"""We abuse the code generator for introspection."""
|
||||
|
||||
def __init__(self, environment):
|
||||
CodeGenerator.__init__(self, environment, '<introspection>',
|
||||
'<introspection>')
|
||||
self.undeclared_identifiers = set()
|
||||
|
||||
def write(self, x):
|
||||
"""Don't write."""
|
||||
|
||||
def pull_locals(self, frame):
|
||||
"""Remember all undeclared identifiers."""
|
||||
self.undeclared_identifiers.update(frame.identifiers.undeclared)
|
||||
|
||||
|
||||
def find_undeclared_variables(ast):
|
||||
"""Returns a set of all variables in the AST that will be looked up from
|
||||
the context at runtime. Because at compile time it's not known which
|
||||
variables will be used depending on the path the execution takes at
|
||||
runtime, all variables are returned.
|
||||
|
||||
>>> from jinja2 import Environment, meta
|
||||
>>> env = Environment()
|
||||
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
|
||||
>>> meta.find_undeclared_variables(ast)
|
||||
set(['bar'])
|
||||
|
||||
.. admonition:: Implementation
|
||||
|
||||
Internally the code generator is used for finding undeclared variables.
|
||||
This is good to know because the code generator might raise a
|
||||
:exc:`TemplateAssertionError` during compilation and as a matter of
|
||||
fact this function can currently raise that exception as well.
|
||||
"""
|
||||
codegen = TrackingCodeGenerator(ast.environment)
|
||||
codegen.visit(ast)
|
||||
return codegen.undeclared_identifiers
|
||||
|
||||
|
||||
def find_referenced_templates(ast):
|
||||
"""Finds all the referenced templates from the AST. This will return an
|
||||
iterator over all the hardcoded template extensions, inclusions and
|
||||
imports. If dynamic inheritance or inclusion is used, `None` will be
|
||||
yielded.
|
||||
|
||||
>>> from jinja2 import Environment, meta
|
||||
>>> env = Environment()
|
||||
>>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
|
||||
>>> list(meta.find_referenced_templates(ast))
|
||||
['layout.html', None]
|
||||
|
||||
This function is useful for dependency tracking. For example if you want
|
||||
to rebuild parts of the website after a layout template has changed.
|
||||
"""
|
||||
for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
|
||||
nodes.Include)):
|
||||
if not isinstance(node.template, nodes.Const):
|
||||
# a tuple with some non consts in there
|
||||
if isinstance(node.template, (nodes.Tuple, nodes.List)):
|
||||
for template_name in node.template.items:
|
||||
# something const, only yield the strings and ignore
|
||||
# non-string consts that really just make no sense
|
||||
if isinstance(template_name, nodes.Const):
|
||||
if isinstance(template_name.value, string_types):
|
||||
yield template_name.value
|
||||
# something dynamic in there
|
||||
else:
|
||||
yield None
|
||||
# something dynamic we don't know about here
|
||||
else:
|
||||
yield None
|
||||
continue
|
||||
# constant is a basestring, direct template name
|
||||
if isinstance(node.template.value, string_types):
|
||||
yield node.template.value
|
||||
# a tuple or list (latter *should* not happen) made of consts,
|
||||
# yield the consts that are strings. We could warn here for
|
||||
# non string values
|
||||
elif isinstance(node, nodes.Include) and \
|
||||
isinstance(node.template.value, (tuple, list)):
|
||||
for template_name in node.template.value:
|
||||
if isinstance(template_name, string_types):
|
||||
yield template_name
|
||||
# something else we don't care about, we could warn here
|
||||
else:
|
||||
yield None
|
||||
Vendored
-914
@@ -1,914 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.nodes
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module implements additional nodes derived from the ast base node.
|
||||
|
||||
It also provides some node tree helper functions like `in_lineno` and
|
||||
`get_nodes` used by the parser and translator in order to normalize
|
||||
python and jinja nodes.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import operator
|
||||
|
||||
from collections import deque
|
||||
from jinja2.utils import Markup
|
||||
from jinja2._compat import next, izip, with_metaclass, text_type, \
|
||||
method_type, function_type
|
||||
|
||||
|
||||
#: the types we support for context functions
|
||||
_context_function_types = (function_type, method_type)
|
||||
|
||||
|
||||
_binop_to_func = {
|
||||
'*': operator.mul,
|
||||
'/': operator.truediv,
|
||||
'//': operator.floordiv,
|
||||
'**': operator.pow,
|
||||
'%': operator.mod,
|
||||
'+': operator.add,
|
||||
'-': operator.sub
|
||||
}
|
||||
|
||||
_uaop_to_func = {
|
||||
'not': operator.not_,
|
||||
'+': operator.pos,
|
||||
'-': operator.neg
|
||||
}
|
||||
|
||||
_cmpop_to_func = {
|
||||
'eq': operator.eq,
|
||||
'ne': operator.ne,
|
||||
'gt': operator.gt,
|
||||
'gteq': operator.ge,
|
||||
'lt': operator.lt,
|
||||
'lteq': operator.le,
|
||||
'in': lambda a, b: a in b,
|
||||
'notin': lambda a, b: a not in b
|
||||
}
|
||||
|
||||
|
||||
class Impossible(Exception):
|
||||
"""Raised if the node could not perform a requested action."""
|
||||
|
||||
|
||||
class NodeType(type):
|
||||
"""A metaclass for nodes that handles the field and attribute
|
||||
inheritance. fields and attributes from the parent class are
|
||||
automatically forwarded to the child."""
|
||||
|
||||
def __new__(cls, name, bases, d):
|
||||
for attr in 'fields', 'attributes':
|
||||
storage = []
|
||||
storage.extend(getattr(bases[0], attr, ()))
|
||||
storage.extend(d.get(attr, ()))
|
||||
assert len(bases) == 1, 'multiple inheritance not allowed'
|
||||
assert len(storage) == len(set(storage)), 'layout conflict'
|
||||
d[attr] = tuple(storage)
|
||||
d.setdefault('abstract', False)
|
||||
return type.__new__(cls, name, bases, d)
|
||||
|
||||
|
||||
class EvalContext(object):
|
||||
"""Holds evaluation time information. Custom attributes can be attached
|
||||
to it in extensions.
|
||||
"""
|
||||
|
||||
def __init__(self, environment, template_name=None):
|
||||
self.environment = environment
|
||||
if callable(environment.autoescape):
|
||||
self.autoescape = environment.autoescape(template_name)
|
||||
else:
|
||||
self.autoescape = environment.autoescape
|
||||
self.volatile = False
|
||||
|
||||
def save(self):
|
||||
return self.__dict__.copy()
|
||||
|
||||
def revert(self, old):
|
||||
self.__dict__.clear()
|
||||
self.__dict__.update(old)
|
||||
|
||||
|
||||
def get_eval_context(node, ctx):
|
||||
if ctx is None:
|
||||
if node.environment is None:
|
||||
raise RuntimeError('if no eval context is passed, the '
|
||||
'node must have an attached '
|
||||
'environment.')
|
||||
return EvalContext(node.environment)
|
||||
return ctx
|
||||
|
||||
|
||||
class Node(with_metaclass(NodeType, object)):
|
||||
"""Baseclass for all Jinja2 nodes. There are a number of nodes available
|
||||
of different types. There are four major types:
|
||||
|
||||
- :class:`Stmt`: statements
|
||||
- :class:`Expr`: expressions
|
||||
- :class:`Helper`: helper nodes
|
||||
- :class:`Template`: the outermost wrapper node
|
||||
|
||||
All nodes have fields and attributes. Fields may be other nodes, lists,
|
||||
or arbitrary values. Fields are passed to the constructor as regular
|
||||
positional arguments, attributes as keyword arguments. Each node has
|
||||
two attributes: `lineno` (the line number of the node) and `environment`.
|
||||
The `environment` attribute is set at the end of the parsing process for
|
||||
all nodes automatically.
|
||||
"""
|
||||
fields = ()
|
||||
attributes = ('lineno', 'environment')
|
||||
abstract = True
|
||||
|
||||
def __init__(self, *fields, **attributes):
|
||||
if self.abstract:
|
||||
raise TypeError('abstract nodes are not instanciable')
|
||||
if fields:
|
||||
if len(fields) != len(self.fields):
|
||||
if not self.fields:
|
||||
raise TypeError('%r takes 0 arguments' %
|
||||
self.__class__.__name__)
|
||||
raise TypeError('%r takes 0 or %d argument%s' % (
|
||||
self.__class__.__name__,
|
||||
len(self.fields),
|
||||
len(self.fields) != 1 and 's' or ''
|
||||
))
|
||||
for name, arg in izip(self.fields, fields):
|
||||
setattr(self, name, arg)
|
||||
for attr in self.attributes:
|
||||
setattr(self, attr, attributes.pop(attr, None))
|
||||
if attributes:
|
||||
raise TypeError('unknown attribute %r' %
|
||||
next(iter(attributes)))
|
||||
|
||||
def iter_fields(self, exclude=None, only=None):
|
||||
"""This method iterates over all fields that are defined and yields
|
||||
``(key, value)`` tuples. Per default all fields are returned, but
|
||||
it's possible to limit that to some fields by providing the `only`
|
||||
parameter or to exclude some using the `exclude` parameter. Both
|
||||
should be sets or tuples of field names.
|
||||
"""
|
||||
for name in self.fields:
|
||||
if (exclude is only is None) or \
|
||||
(exclude is not None and name not in exclude) or \
|
||||
(only is not None and name in only):
|
||||
try:
|
||||
yield name, getattr(self, name)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def iter_child_nodes(self, exclude=None, only=None):
|
||||
"""Iterates over all direct child nodes of the node. This iterates
|
||||
over all fields and yields the values of they are nodes. If the value
|
||||
of a field is a list all the nodes in that list are returned.
|
||||
"""
|
||||
for field, item in self.iter_fields(exclude, only):
|
||||
if isinstance(item, list):
|
||||
for n in item:
|
||||
if isinstance(n, Node):
|
||||
yield n
|
||||
elif isinstance(item, Node):
|
||||
yield item
|
||||
|
||||
def find(self, node_type):
|
||||
"""Find the first node of a given type. If no such node exists the
|
||||
return value is `None`.
|
||||
"""
|
||||
for result in self.find_all(node_type):
|
||||
return result
|
||||
|
||||
def find_all(self, node_type):
|
||||
"""Find all the nodes of a given type. If the type is a tuple,
|
||||
the check is performed for any of the tuple items.
|
||||
"""
|
||||
for child in self.iter_child_nodes():
|
||||
if isinstance(child, node_type):
|
||||
yield child
|
||||
for result in child.find_all(node_type):
|
||||
yield result
|
||||
|
||||
def set_ctx(self, ctx):
|
||||
"""Reset the context of a node and all child nodes. Per default the
|
||||
parser will all generate nodes that have a 'load' context as it's the
|
||||
most common one. This method is used in the parser to set assignment
|
||||
targets and other nodes to a store context.
|
||||
"""
|
||||
todo = deque([self])
|
||||
while todo:
|
||||
node = todo.popleft()
|
||||
if 'ctx' in node.fields:
|
||||
node.ctx = ctx
|
||||
todo.extend(node.iter_child_nodes())
|
||||
return self
|
||||
|
||||
def set_lineno(self, lineno, override=False):
|
||||
"""Set the line numbers of the node and children."""
|
||||
todo = deque([self])
|
||||
while todo:
|
||||
node = todo.popleft()
|
||||
if 'lineno' in node.attributes:
|
||||
if node.lineno is None or override:
|
||||
node.lineno = lineno
|
||||
todo.extend(node.iter_child_nodes())
|
||||
return self
|
||||
|
||||
def set_environment(self, environment):
|
||||
"""Set the environment for all nodes."""
|
||||
todo = deque([self])
|
||||
while todo:
|
||||
node = todo.popleft()
|
||||
node.environment = environment
|
||||
todo.extend(node.iter_child_nodes())
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) is type(other) and \
|
||||
tuple(self.iter_fields()) == tuple(other.iter_fields())
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
# Restore Python 2 hashing behavior on Python 3
|
||||
__hash__ = object.__hash__
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (
|
||||
self.__class__.__name__,
|
||||
', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
|
||||
arg in self.fields)
|
||||
)
|
||||
|
||||
|
||||
class Stmt(Node):
|
||||
"""Base node for all statements."""
|
||||
abstract = True
|
||||
|
||||
|
||||
class Helper(Node):
|
||||
"""Nodes that exist in a specific context only."""
|
||||
abstract = True
|
||||
|
||||
|
||||
class Template(Node):
|
||||
"""Node that represents a template. This must be the outermost node that
|
||||
is passed to the compiler.
|
||||
"""
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
class Output(Stmt):
|
||||
"""A node that holds multiple expressions which are then printed out.
|
||||
This is used both for the `print` statement and the regular template data.
|
||||
"""
|
||||
fields = ('nodes',)
|
||||
|
||||
|
||||
class Extends(Stmt):
|
||||
"""Represents an extends statement."""
|
||||
fields = ('template',)
|
||||
|
||||
|
||||
class For(Stmt):
|
||||
"""The for loop. `target` is the target for the iteration (usually a
|
||||
:class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
|
||||
of nodes that are used as loop-body, and `else_` a list of nodes for the
|
||||
`else` block. If no else node exists it has to be an empty list.
|
||||
|
||||
For filtered nodes an expression can be stored as `test`, otherwise `None`.
|
||||
"""
|
||||
fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
|
||||
|
||||
|
||||
class If(Stmt):
|
||||
"""If `test` is true, `body` is rendered, else `else_`."""
|
||||
fields = ('test', 'body', 'else_')
|
||||
|
||||
|
||||
class Macro(Stmt):
|
||||
"""A macro definition. `name` is the name of the macro, `args` a list of
|
||||
arguments and `defaults` a list of defaults if there are any. `body` is
|
||||
a list of nodes for the macro body.
|
||||
"""
|
||||
fields = ('name', 'args', 'defaults', 'body')
|
||||
|
||||
|
||||
class CallBlock(Stmt):
|
||||
"""Like a macro without a name but a call instead. `call` is called with
|
||||
the unnamed macro as `caller` argument this node holds.
|
||||
"""
|
||||
fields = ('call', 'args', 'defaults', 'body')
|
||||
|
||||
|
||||
class FilterBlock(Stmt):
|
||||
"""Node for filter sections."""
|
||||
fields = ('body', 'filter')
|
||||
|
||||
|
||||
class Block(Stmt):
|
||||
"""A node that represents a block."""
|
||||
fields = ('name', 'body', 'scoped')
|
||||
|
||||
|
||||
class Include(Stmt):
|
||||
"""A node that represents the include tag."""
|
||||
fields = ('template', 'with_context', 'ignore_missing')
|
||||
|
||||
|
||||
class Import(Stmt):
|
||||
"""A node that represents the import tag."""
|
||||
fields = ('template', 'target', 'with_context')
|
||||
|
||||
|
||||
class FromImport(Stmt):
|
||||
"""A node that represents the from import tag. It's important to not
|
||||
pass unsafe names to the name attribute. The compiler translates the
|
||||
attribute lookups directly into getattr calls and does *not* use the
|
||||
subscript callback of the interface. As exported variables may not
|
||||
start with double underscores (which the parser asserts) this is not a
|
||||
problem for regular Jinja code, but if this node is used in an extension
|
||||
extra care must be taken.
|
||||
|
||||
The list of names may contain tuples if aliases are wanted.
|
||||
"""
|
||||
fields = ('template', 'names', 'with_context')
|
||||
|
||||
|
||||
class ExprStmt(Stmt):
|
||||
"""A statement that evaluates an expression and discards the result."""
|
||||
fields = ('node',)
|
||||
|
||||
|
||||
class Assign(Stmt):
|
||||
"""Assigns an expression to a target."""
|
||||
fields = ('target', 'node')
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
"""Baseclass for all expressions."""
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
"""Return the value of the expression as constant or raise
|
||||
:exc:`Impossible` if this was not possible.
|
||||
|
||||
An :class:`EvalContext` can be provided, if none is given
|
||||
a default context is created which requires the nodes to have
|
||||
an attached environment.
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
the `eval_ctx` parameter was added.
|
||||
"""
|
||||
raise Impossible()
|
||||
|
||||
def can_assign(self):
|
||||
"""Check if it's possible to assign something to this node."""
|
||||
return False
|
||||
|
||||
|
||||
class BinExpr(Expr):
|
||||
"""Baseclass for all binary expressions."""
|
||||
fields = ('left', 'right')
|
||||
operator = None
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
# intercepted operators cannot be folded at compile time
|
||||
if self.environment.sandboxed and \
|
||||
self.operator in self.environment.intercepted_binops:
|
||||
raise Impossible()
|
||||
f = _binop_to_func[self.operator]
|
||||
try:
|
||||
return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
|
||||
class UnaryExpr(Expr):
|
||||
"""Baseclass for all unary expressions."""
|
||||
fields = ('node',)
|
||||
operator = None
|
||||
abstract = True
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
# intercepted operators cannot be folded at compile time
|
||||
if self.environment.sandboxed and \
|
||||
self.operator in self.environment.intercepted_unops:
|
||||
raise Impossible()
|
||||
f = _uaop_to_func[self.operator]
|
||||
try:
|
||||
return f(self.node.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
|
||||
class Name(Expr):
|
||||
"""Looks up a name or stores a value in a name.
|
||||
The `ctx` of the node can be one of the following values:
|
||||
|
||||
- `store`: store a value in the name
|
||||
- `load`: load that name
|
||||
- `param`: like `store` but if the name was defined as function parameter.
|
||||
"""
|
||||
fields = ('name', 'ctx')
|
||||
|
||||
def can_assign(self):
|
||||
return self.name not in ('true', 'false', 'none',
|
||||
'True', 'False', 'None')
|
||||
|
||||
|
||||
class Literal(Expr):
|
||||
"""Baseclass for literals."""
|
||||
abstract = True
|
||||
|
||||
|
||||
class Const(Literal):
|
||||
"""All constant values. The parser will return this node for simple
|
||||
constants such as ``42`` or ``"foo"`` but it can be used to store more
|
||||
complex values such as lists too. Only constants with a safe
|
||||
representation (objects where ``eval(repr(x)) == x`` is true).
|
||||
"""
|
||||
fields = ('value',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_untrusted(cls, value, lineno=None, environment=None):
|
||||
"""Return a const object if the value is representable as
|
||||
constant value in the generated code, otherwise it will raise
|
||||
an `Impossible` exception.
|
||||
"""
|
||||
from .compiler import has_safe_repr
|
||||
if not has_safe_repr(value):
|
||||
raise Impossible()
|
||||
return cls(value, lineno=lineno, environment=environment)
|
||||
|
||||
|
||||
class TemplateData(Literal):
|
||||
"""A constant template string."""
|
||||
fields = ('data',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if eval_ctx.volatile:
|
||||
raise Impossible()
|
||||
if eval_ctx.autoescape:
|
||||
return Markup(self.data)
|
||||
return self.data
|
||||
|
||||
|
||||
class Tuple(Literal):
|
||||
"""For loop unpacking and some other things like multiple arguments
|
||||
for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
|
||||
is used for loading the names or storing.
|
||||
"""
|
||||
fields = ('items', 'ctx')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return tuple(x.as_const(eval_ctx) for x in self.items)
|
||||
|
||||
def can_assign(self):
|
||||
for item in self.items:
|
||||
if not item.can_assign():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class List(Literal):
|
||||
"""Any list literal such as ``[1, 2, 3]``"""
|
||||
fields = ('items',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return [x.as_const(eval_ctx) for x in self.items]
|
||||
|
||||
|
||||
class Dict(Literal):
|
||||
"""Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
|
||||
:class:`Pair` nodes.
|
||||
"""
|
||||
fields = ('items',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return dict(x.as_const(eval_ctx) for x in self.items)
|
||||
|
||||
|
||||
class Pair(Helper):
|
||||
"""A key, value pair for dicts."""
|
||||
fields = ('key', 'value')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
|
||||
|
||||
|
||||
class Keyword(Helper):
|
||||
"""A key, value pair for keyword arguments where key is a string."""
|
||||
fields = ('key', 'value')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.key, self.value.as_const(eval_ctx)
|
||||
|
||||
|
||||
class CondExpr(Expr):
|
||||
"""A conditional expression (inline if expression). (``{{
|
||||
foo if bar else baz }}``)
|
||||
"""
|
||||
fields = ('test', 'expr1', 'expr2')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if self.test.as_const(eval_ctx):
|
||||
return self.expr1.as_const(eval_ctx)
|
||||
|
||||
# if we evaluate to an undefined object, we better do that at runtime
|
||||
if self.expr2 is None:
|
||||
raise Impossible()
|
||||
|
||||
return self.expr2.as_const(eval_ctx)
|
||||
|
||||
|
||||
class Filter(Expr):
|
||||
"""This node applies a filter on an expression. `name` is the name of
|
||||
the filter, the rest of the fields are the same as for :class:`Call`.
|
||||
|
||||
If the `node` of a filter is `None` the contents of the last buffer are
|
||||
filtered. Buffers are created by macros and filter blocks.
|
||||
"""
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if eval_ctx.volatile or self.node is None:
|
||||
raise Impossible()
|
||||
# we have to be careful here because we call filter_ below.
|
||||
# if this variable would be called filter, 2to3 would wrap the
|
||||
# call in a list beause it is assuming we are talking about the
|
||||
# builtin filter function here which no longer returns a list in
|
||||
# python 3. because of that, do not rename filter_ to filter!
|
||||
filter_ = self.environment.filters.get(self.name)
|
||||
if filter_ is None or getattr(filter_, 'contextfilter', False):
|
||||
raise Impossible()
|
||||
obj = self.node.as_const(eval_ctx)
|
||||
args = [x.as_const(eval_ctx) for x in self.args]
|
||||
if getattr(filter_, 'evalcontextfilter', False):
|
||||
args.insert(0, eval_ctx)
|
||||
elif getattr(filter_, 'environmentfilter', False):
|
||||
args.insert(0, self.environment)
|
||||
kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
|
||||
if self.dyn_args is not None:
|
||||
try:
|
||||
args.extend(self.dyn_args.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
if self.dyn_kwargs is not None:
|
||||
try:
|
||||
kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
try:
|
||||
return filter_(obj, *args, **kwargs)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
|
||||
class Test(Expr):
|
||||
"""Applies a test on an expression. `name` is the name of the test, the
|
||||
rest of the fields are the same as for :class:`Call`.
|
||||
"""
|
||||
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
|
||||
class Call(Expr):
|
||||
"""Calls an expression. `args` is a list of arguments, `kwargs` a list
|
||||
of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
|
||||
and `dyn_kwargs` has to be either `None` or a node that is used as
|
||||
node for dynamic positional (``*args``) or keyword (``**kwargs``)
|
||||
arguments.
|
||||
"""
|
||||
fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if eval_ctx.volatile:
|
||||
raise Impossible()
|
||||
obj = self.node.as_const(eval_ctx)
|
||||
|
||||
# don't evaluate context functions
|
||||
args = [x.as_const(eval_ctx) for x in self.args]
|
||||
if isinstance(obj, _context_function_types):
|
||||
if getattr(obj, 'contextfunction', False):
|
||||
raise Impossible()
|
||||
elif getattr(obj, 'evalcontextfunction', False):
|
||||
args.insert(0, eval_ctx)
|
||||
elif getattr(obj, 'environmentfunction', False):
|
||||
args.insert(0, self.environment)
|
||||
|
||||
kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
|
||||
if self.dyn_args is not None:
|
||||
try:
|
||||
args.extend(self.dyn_args.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
if self.dyn_kwargs is not None:
|
||||
try:
|
||||
kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
try:
|
||||
return obj(*args, **kwargs)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
|
||||
class Getitem(Expr):
|
||||
"""Get an attribute or item from an expression and prefer the item."""
|
||||
fields = ('node', 'arg', 'ctx')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if self.ctx != 'load':
|
||||
raise Impossible()
|
||||
try:
|
||||
return self.environment.getitem(self.node.as_const(eval_ctx),
|
||||
self.arg.as_const(eval_ctx))
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
def can_assign(self):
|
||||
return False
|
||||
|
||||
|
||||
class Getattr(Expr):
|
||||
"""Get an attribute or item from an expression that is a ascii-only
|
||||
bytestring and prefer the attribute.
|
||||
"""
|
||||
fields = ('node', 'attr', 'ctx')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
if self.ctx != 'load':
|
||||
raise Impossible()
|
||||
try:
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.environment.getattr(self.node.as_const(eval_ctx),
|
||||
self.attr)
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
|
||||
def can_assign(self):
|
||||
return False
|
||||
|
||||
|
||||
class Slice(Expr):
|
||||
"""Represents a slice object. This must only be used as argument for
|
||||
:class:`Subscript`.
|
||||
"""
|
||||
fields = ('start', 'stop', 'step')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
def const(obj):
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.as_const(eval_ctx)
|
||||
return slice(const(self.start), const(self.stop), const(self.step))
|
||||
|
||||
|
||||
class Concat(Expr):
|
||||
"""Concatenates the list of expressions provided after converting them to
|
||||
unicode.
|
||||
"""
|
||||
fields = ('nodes',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
|
||||
|
||||
|
||||
class Compare(Expr):
|
||||
"""Compares an expression with some other expressions. `ops` must be a
|
||||
list of :class:`Operand`\s.
|
||||
"""
|
||||
fields = ('expr', 'ops')
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
result = value = self.expr.as_const(eval_ctx)
|
||||
try:
|
||||
for op in self.ops:
|
||||
new_value = op.expr.as_const(eval_ctx)
|
||||
result = _cmpop_to_func[op.op](value, new_value)
|
||||
value = new_value
|
||||
except Exception:
|
||||
raise Impossible()
|
||||
return result
|
||||
|
||||
|
||||
class Operand(Helper):
|
||||
"""Holds an operator and an expression."""
|
||||
fields = ('op', 'expr')
|
||||
|
||||
if __debug__:
|
||||
Operand.__doc__ += '\nThe following operators are available: ' + \
|
||||
', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
|
||||
set(_uaop_to_func) | set(_cmpop_to_func)))
|
||||
|
||||
|
||||
class Mul(BinExpr):
|
||||
"""Multiplies the left with the right node."""
|
||||
operator = '*'
|
||||
|
||||
|
||||
class Div(BinExpr):
|
||||
"""Divides the left by the right node."""
|
||||
operator = '/'
|
||||
|
||||
|
||||
class FloorDiv(BinExpr):
|
||||
"""Divides the left by the right node and truncates conver the
|
||||
result into an integer by truncating.
|
||||
"""
|
||||
operator = '//'
|
||||
|
||||
|
||||
class Add(BinExpr):
|
||||
"""Add the left to the right node."""
|
||||
operator = '+'
|
||||
|
||||
|
||||
class Sub(BinExpr):
|
||||
"""Substract the right from the left node."""
|
||||
operator = '-'
|
||||
|
||||
|
||||
class Mod(BinExpr):
|
||||
"""Left modulo right."""
|
||||
operator = '%'
|
||||
|
||||
|
||||
class Pow(BinExpr):
|
||||
"""Left to the power of right."""
|
||||
operator = '**'
|
||||
|
||||
|
||||
class And(BinExpr):
|
||||
"""Short circuited AND."""
|
||||
operator = 'and'
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
|
||||
|
||||
|
||||
class Or(BinExpr):
|
||||
"""Short circuited OR."""
|
||||
operator = 'or'
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
|
||||
|
||||
|
||||
class Not(UnaryExpr):
|
||||
"""Negate the expression."""
|
||||
operator = 'not'
|
||||
|
||||
|
||||
class Neg(UnaryExpr):
|
||||
"""Make the expression negative."""
|
||||
operator = '-'
|
||||
|
||||
|
||||
class Pos(UnaryExpr):
|
||||
"""Make the expression positive (noop for most expressions)"""
|
||||
operator = '+'
|
||||
|
||||
|
||||
# Helpers for extensions
|
||||
|
||||
|
||||
class EnvironmentAttribute(Expr):
|
||||
"""Loads an attribute from the environment object. This is useful for
|
||||
extensions that want to call a callback stored on the environment.
|
||||
"""
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class ExtensionAttribute(Expr):
|
||||
"""Returns the attribute of an extension bound to the environment.
|
||||
The identifier is the identifier of the :class:`Extension`.
|
||||
|
||||
This node is usually constructed by calling the
|
||||
:meth:`~jinja2.ext.Extension.attr` method on an extension.
|
||||
"""
|
||||
fields = ('identifier', 'name')
|
||||
|
||||
|
||||
class ImportedName(Expr):
|
||||
"""If created with an import name the import name is returned on node
|
||||
access. For example ``ImportedName('cgi.escape')`` returns the `escape`
|
||||
function from the cgi module on evaluation. Imports are optimized by the
|
||||
compiler so there is no need to assign them to local variables.
|
||||
"""
|
||||
fields = ('importname',)
|
||||
|
||||
|
||||
class InternalName(Expr):
|
||||
"""An internal name in the compiler. You cannot create these nodes
|
||||
yourself but the parser provides a
|
||||
:meth:`~jinja2.parser.Parser.free_identifier` method that creates
|
||||
a new identifier for you. This identifier is not available from the
|
||||
template and is not threated specially by the compiler.
|
||||
"""
|
||||
fields = ('name',)
|
||||
|
||||
def __init__(self):
|
||||
raise TypeError('Can\'t create internal names. Use the '
|
||||
'`free_identifier` method on a parser.')
|
||||
|
||||
|
||||
class MarkSafe(Expr):
|
||||
"""Mark the wrapped expression as safe (wrap it as `Markup`)."""
|
||||
fields = ('expr',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
return Markup(self.expr.as_const(eval_ctx))
|
||||
|
||||
|
||||
class MarkSafeIfAutoescape(Expr):
|
||||
"""Mark the wrapped expression as safe (wrap it as `Markup`) but
|
||||
only if autoescaping is active.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
"""
|
||||
fields = ('expr',)
|
||||
|
||||
def as_const(self, eval_ctx=None):
|
||||
eval_ctx = get_eval_context(self, eval_ctx)
|
||||
if eval_ctx.volatile:
|
||||
raise Impossible()
|
||||
expr = self.expr.as_const(eval_ctx)
|
||||
if eval_ctx.autoescape:
|
||||
return Markup(expr)
|
||||
return expr
|
||||
|
||||
|
||||
class ContextReference(Expr):
|
||||
"""Returns the current template context. It can be used like a
|
||||
:class:`Name` node, with a ``'load'`` ctx and will return the
|
||||
current :class:`~jinja2.runtime.Context` object.
|
||||
|
||||
Here an example that assigns the current template name to a
|
||||
variable named `foo`::
|
||||
|
||||
Assign(Name('foo', ctx='store'),
|
||||
Getattr(ContextReference(), 'name'))
|
||||
"""
|
||||
|
||||
|
||||
class Continue(Stmt):
|
||||
"""Continue a loop."""
|
||||
|
||||
|
||||
class Break(Stmt):
|
||||
"""Break a loop."""
|
||||
|
||||
|
||||
class Scope(Stmt):
|
||||
"""An artificial scope."""
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
class EvalContextModifier(Stmt):
|
||||
"""Modifies the eval context. For each option that should be modified,
|
||||
a :class:`Keyword` has to be added to the :attr:`options` list.
|
||||
|
||||
Example to change the `autoescape` setting::
|
||||
|
||||
EvalContextModifier(options=[Keyword('autoescape', Const(True))])
|
||||
"""
|
||||
fields = ('options',)
|
||||
|
||||
|
||||
class ScopedEvalContextModifier(EvalContextModifier):
|
||||
"""Modifies the eval context and reverts it later. Works exactly like
|
||||
:class:`EvalContextModifier` but will only modify the
|
||||
:class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
|
||||
"""
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
# make sure nobody creates custom nodes
|
||||
def _failing_new(*args, **kwargs):
|
||||
raise TypeError('can\'t create custom node types')
|
||||
NodeType.__new__ = staticmethod(_failing_new); del _failing_new
|
||||
Vendored
-68
@@ -1,68 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.optimizer
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The jinja optimizer is currently trying to constant fold a few expressions
|
||||
and modify the AST in place so that it should be easier to evaluate it.
|
||||
|
||||
Because the AST does not contain all the scoping information and the
|
||||
compiler has to find that out, we cannot do all the optimizations we
|
||||
want. For example loop unrolling doesn't work because unrolled loops would
|
||||
have a different scoping.
|
||||
|
||||
The solution would be a second syntax tree that has the scoping rules stored.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.visitor import NodeTransformer
|
||||
|
||||
|
||||
def optimize(node, environment):
|
||||
"""The context hint can be used to perform an static optimization
|
||||
based on the context given."""
|
||||
optimizer = Optimizer(environment)
|
||||
return optimizer.visit(node)
|
||||
|
||||
|
||||
class Optimizer(NodeTransformer):
|
||||
|
||||
def __init__(self, environment):
|
||||
self.environment = environment
|
||||
|
||||
def visit_If(self, node):
|
||||
"""Eliminate dead code."""
|
||||
# do not optimize ifs that have a block inside so that it doesn't
|
||||
# break super().
|
||||
if node.find(nodes.Block) is not None:
|
||||
return self.generic_visit(node)
|
||||
try:
|
||||
val = self.visit(node.test).as_const()
|
||||
except nodes.Impossible:
|
||||
return self.generic_visit(node)
|
||||
if val:
|
||||
body = node.body
|
||||
else:
|
||||
body = node.else_
|
||||
result = []
|
||||
for node in body:
|
||||
result.extend(self.visit_list(node))
|
||||
return result
|
||||
|
||||
def fold(self, node):
|
||||
"""Do constant folding."""
|
||||
node = self.generic_visit(node)
|
||||
try:
|
||||
return nodes.Const.from_untrusted(node.as_const(),
|
||||
lineno=node.lineno,
|
||||
environment=self.environment)
|
||||
except nodes.Impossible:
|
||||
return node
|
||||
|
||||
visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
|
||||
visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
|
||||
visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
|
||||
visit_Filter = visit_Test = visit_CondExpr = fold
|
||||
del fold
|
||||
Vendored
-895
@@ -1,895 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.parser
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements the template parser.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from jinja2 import nodes
|
||||
from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
|
||||
from jinja2.lexer import describe_token, describe_token_expr
|
||||
from jinja2._compat import next, imap
|
||||
|
||||
|
||||
#: statements that callinto
|
||||
_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
|
||||
'macro', 'include', 'from', 'import',
|
||||
'set'])
|
||||
_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
|
||||
|
||||
|
||||
class Parser(object):
|
||||
"""This is the central parsing class Jinja2 uses. It's passed to
|
||||
extensions and can be used to parse expressions or statements.
|
||||
"""
|
||||
|
||||
def __init__(self, environment, source, name=None, filename=None,
|
||||
state=None):
|
||||
self.environment = environment
|
||||
self.stream = environment._tokenize(source, name, filename, state)
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
self.extensions = {}
|
||||
for extension in environment.iter_extensions():
|
||||
for tag in extension.tags:
|
||||
self.extensions[tag] = extension.parse
|
||||
self._last_identifier = 0
|
||||
self._tag_stack = []
|
||||
self._end_token_stack = []
|
||||
|
||||
def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
|
||||
"""Convenience method that raises `exc` with the message, passed
|
||||
line number or last line number as well as the current name and
|
||||
filename.
|
||||
"""
|
||||
if lineno is None:
|
||||
lineno = self.stream.current.lineno
|
||||
raise exc(msg, lineno, self.name, self.filename)
|
||||
|
||||
def _fail_ut_eof(self, name, end_token_stack, lineno):
|
||||
expected = []
|
||||
for exprs in end_token_stack:
|
||||
expected.extend(imap(describe_token_expr, exprs))
|
||||
if end_token_stack:
|
||||
currently_looking = ' or '.join(
|
||||
"'%s'" % describe_token_expr(expr)
|
||||
for expr in end_token_stack[-1])
|
||||
else:
|
||||
currently_looking = None
|
||||
|
||||
if name is None:
|
||||
message = ['Unexpected end of template.']
|
||||
else:
|
||||
message = ['Encountered unknown tag \'%s\'.' % name]
|
||||
|
||||
if currently_looking:
|
||||
if name is not None and name in expected:
|
||||
message.append('You probably made a nesting mistake. Jinja '
|
||||
'is expecting this tag, but currently looking '
|
||||
'for %s.' % currently_looking)
|
||||
else:
|
||||
message.append('Jinja was looking for the following tags: '
|
||||
'%s.' % currently_looking)
|
||||
|
||||
if self._tag_stack:
|
||||
message.append('The innermost block that needs to be '
|
||||
'closed is \'%s\'.' % self._tag_stack[-1])
|
||||
|
||||
self.fail(' '.join(message), lineno)
|
||||
|
||||
def fail_unknown_tag(self, name, lineno=None):
|
||||
"""Called if the parser encounters an unknown tag. Tries to fail
|
||||
with a human readable error message that could help to identify
|
||||
the problem.
|
||||
"""
|
||||
return self._fail_ut_eof(name, self._end_token_stack, lineno)
|
||||
|
||||
def fail_eof(self, end_tokens=None, lineno=None):
|
||||
"""Like fail_unknown_tag but for end of template situations."""
|
||||
stack = list(self._end_token_stack)
|
||||
if end_tokens is not None:
|
||||
stack.append(end_tokens)
|
||||
return self._fail_ut_eof(None, stack, lineno)
|
||||
|
||||
def is_tuple_end(self, extra_end_rules=None):
|
||||
"""Are we at the end of a tuple?"""
|
||||
if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
|
||||
return True
|
||||
elif extra_end_rules is not None:
|
||||
return self.stream.current.test_any(extra_end_rules)
|
||||
return False
|
||||
|
||||
def free_identifier(self, lineno=None):
|
||||
"""Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
|
||||
self._last_identifier += 1
|
||||
rv = object.__new__(nodes.InternalName)
|
||||
nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
|
||||
return rv
|
||||
|
||||
def parse_statement(self):
|
||||
"""Parse a single statement."""
|
||||
token = self.stream.current
|
||||
if token.type != 'name':
|
||||
self.fail('tag name expected', token.lineno)
|
||||
self._tag_stack.append(token.value)
|
||||
pop_tag = True
|
||||
try:
|
||||
if token.value in _statement_keywords:
|
||||
return getattr(self, 'parse_' + self.stream.current.value)()
|
||||
if token.value == 'call':
|
||||
return self.parse_call_block()
|
||||
if token.value == 'filter':
|
||||
return self.parse_filter_block()
|
||||
ext = self.extensions.get(token.value)
|
||||
if ext is not None:
|
||||
return ext(self)
|
||||
|
||||
# did not work out, remove the token we pushed by accident
|
||||
# from the stack so that the unknown tag fail function can
|
||||
# produce a proper error message.
|
||||
self._tag_stack.pop()
|
||||
pop_tag = False
|
||||
self.fail_unknown_tag(token.value, token.lineno)
|
||||
finally:
|
||||
if pop_tag:
|
||||
self._tag_stack.pop()
|
||||
|
||||
def parse_statements(self, end_tokens, drop_needle=False):
|
||||
"""Parse multiple statements into a list until one of the end tokens
|
||||
is reached. This is used to parse the body of statements as it also
|
||||
parses template data if appropriate. The parser checks first if the
|
||||
current token is a colon and skips it if there is one. Then it checks
|
||||
for the block end and parses until if one of the `end_tokens` is
|
||||
reached. Per default the active token in the stream at the end of
|
||||
the call is the matched end token. If this is not wanted `drop_needle`
|
||||
can be set to `True` and the end token is removed.
|
||||
"""
|
||||
# the first token may be a colon for python compatibility
|
||||
self.stream.skip_if('colon')
|
||||
|
||||
# in the future it would be possible to add whole code sections
|
||||
# by adding some sort of end of statement token and parsing those here.
|
||||
self.stream.expect('block_end')
|
||||
result = self.subparse(end_tokens)
|
||||
|
||||
# we reached the end of the template too early, the subparser
|
||||
# does not check for this, so we do that now
|
||||
if self.stream.current.type == 'eof':
|
||||
self.fail_eof(end_tokens)
|
||||
|
||||
if drop_needle:
|
||||
next(self.stream)
|
||||
return result
|
||||
|
||||
def parse_set(self):
|
||||
"""Parse an assign statement."""
|
||||
lineno = next(self.stream).lineno
|
||||
target = self.parse_assign_target()
|
||||
self.stream.expect('assign')
|
||||
expr = self.parse_tuple()
|
||||
return nodes.Assign(target, expr, lineno=lineno)
|
||||
|
||||
def parse_for(self):
|
||||
"""Parse a for loop."""
|
||||
lineno = self.stream.expect('name:for').lineno
|
||||
target = self.parse_assign_target(extra_end_rules=('name:in',))
|
||||
self.stream.expect('name:in')
|
||||
iter = self.parse_tuple(with_condexpr=False,
|
||||
extra_end_rules=('name:recursive',))
|
||||
test = None
|
||||
if self.stream.skip_if('name:if'):
|
||||
test = self.parse_expression()
|
||||
recursive = self.stream.skip_if('name:recursive')
|
||||
body = self.parse_statements(('name:endfor', 'name:else'))
|
||||
if next(self.stream).value == 'endfor':
|
||||
else_ = []
|
||||
else:
|
||||
else_ = self.parse_statements(('name:endfor',), drop_needle=True)
|
||||
return nodes.For(target, iter, body, else_, test,
|
||||
recursive, lineno=lineno)
|
||||
|
||||
def parse_if(self):
|
||||
"""Parse an if construct."""
|
||||
node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
|
||||
while 1:
|
||||
node.test = self.parse_tuple(with_condexpr=False)
|
||||
node.body = self.parse_statements(('name:elif', 'name:else',
|
||||
'name:endif'))
|
||||
token = next(self.stream)
|
||||
if token.test('name:elif'):
|
||||
new_node = nodes.If(lineno=self.stream.current.lineno)
|
||||
node.else_ = [new_node]
|
||||
node = new_node
|
||||
continue
|
||||
elif token.test('name:else'):
|
||||
node.else_ = self.parse_statements(('name:endif',),
|
||||
drop_needle=True)
|
||||
else:
|
||||
node.else_ = []
|
||||
break
|
||||
return result
|
||||
|
||||
def parse_block(self):
|
||||
node = nodes.Block(lineno=next(self.stream).lineno)
|
||||
node.name = self.stream.expect('name').value
|
||||
node.scoped = self.stream.skip_if('name:scoped')
|
||||
|
||||
# common problem people encounter when switching from django
|
||||
# to jinja. we do not support hyphens in block names, so let's
|
||||
# raise a nicer error message in that case.
|
||||
if self.stream.current.type == 'sub':
|
||||
self.fail('Block names in Jinja have to be valid Python '
|
||||
'identifiers and may not contain hyphens, use an '
|
||||
'underscore instead.')
|
||||
|
||||
node.body = self.parse_statements(('name:endblock',), drop_needle=True)
|
||||
self.stream.skip_if('name:' + node.name)
|
||||
return node
|
||||
|
||||
def parse_extends(self):
|
||||
node = nodes.Extends(lineno=next(self.stream).lineno)
|
||||
node.template = self.parse_expression()
|
||||
return node
|
||||
|
||||
def parse_import_context(self, node, default):
|
||||
if self.stream.current.test_any('name:with', 'name:without') and \
|
||||
self.stream.look().test('name:context'):
|
||||
node.with_context = next(self.stream).value == 'with'
|
||||
self.stream.skip()
|
||||
else:
|
||||
node.with_context = default
|
||||
return node
|
||||
|
||||
def parse_include(self):
|
||||
node = nodes.Include(lineno=next(self.stream).lineno)
|
||||
node.template = self.parse_expression()
|
||||
if self.stream.current.test('name:ignore') and \
|
||||
self.stream.look().test('name:missing'):
|
||||
node.ignore_missing = True
|
||||
self.stream.skip(2)
|
||||
else:
|
||||
node.ignore_missing = False
|
||||
return self.parse_import_context(node, True)
|
||||
|
||||
def parse_import(self):
|
||||
node = nodes.Import(lineno=next(self.stream).lineno)
|
||||
node.template = self.parse_expression()
|
||||
self.stream.expect('name:as')
|
||||
node.target = self.parse_assign_target(name_only=True).name
|
||||
return self.parse_import_context(node, False)
|
||||
|
||||
def parse_from(self):
|
||||
node = nodes.FromImport(lineno=next(self.stream).lineno)
|
||||
node.template = self.parse_expression()
|
||||
self.stream.expect('name:import')
|
||||
node.names = []
|
||||
|
||||
def parse_context():
|
||||
if self.stream.current.value in ('with', 'without') and \
|
||||
self.stream.look().test('name:context'):
|
||||
node.with_context = next(self.stream).value == 'with'
|
||||
self.stream.skip()
|
||||
return True
|
||||
return False
|
||||
|
||||
while 1:
|
||||
if node.names:
|
||||
self.stream.expect('comma')
|
||||
if self.stream.current.type == 'name':
|
||||
if parse_context():
|
||||
break
|
||||
target = self.parse_assign_target(name_only=True)
|
||||
if target.name.startswith('_'):
|
||||
self.fail('names starting with an underline can not '
|
||||
'be imported', target.lineno,
|
||||
exc=TemplateAssertionError)
|
||||
if self.stream.skip_if('name:as'):
|
||||
alias = self.parse_assign_target(name_only=True)
|
||||
node.names.append((target.name, alias.name))
|
||||
else:
|
||||
node.names.append(target.name)
|
||||
if parse_context() or self.stream.current.type != 'comma':
|
||||
break
|
||||
else:
|
||||
break
|
||||
if not hasattr(node, 'with_context'):
|
||||
node.with_context = False
|
||||
self.stream.skip_if('comma')
|
||||
return node
|
||||
|
||||
def parse_signature(self, node):
|
||||
node.args = args = []
|
||||
node.defaults = defaults = []
|
||||
self.stream.expect('lparen')
|
||||
while self.stream.current.type != 'rparen':
|
||||
if args:
|
||||
self.stream.expect('comma')
|
||||
arg = self.parse_assign_target(name_only=True)
|
||||
arg.set_ctx('param')
|
||||
if self.stream.skip_if('assign'):
|
||||
defaults.append(self.parse_expression())
|
||||
args.append(arg)
|
||||
self.stream.expect('rparen')
|
||||
|
||||
def parse_call_block(self):
|
||||
node = nodes.CallBlock(lineno=next(self.stream).lineno)
|
||||
if self.stream.current.type == 'lparen':
|
||||
self.parse_signature(node)
|
||||
else:
|
||||
node.args = []
|
||||
node.defaults = []
|
||||
|
||||
node.call = self.parse_expression()
|
||||
if not isinstance(node.call, nodes.Call):
|
||||
self.fail('expected call', node.lineno)
|
||||
node.body = self.parse_statements(('name:endcall',), drop_needle=True)
|
||||
return node
|
||||
|
||||
def parse_filter_block(self):
|
||||
node = nodes.FilterBlock(lineno=next(self.stream).lineno)
|
||||
node.filter = self.parse_filter(None, start_inline=True)
|
||||
node.body = self.parse_statements(('name:endfilter',),
|
||||
drop_needle=True)
|
||||
return node
|
||||
|
||||
def parse_macro(self):
|
||||
node = nodes.Macro(lineno=next(self.stream).lineno)
|
||||
node.name = self.parse_assign_target(name_only=True).name
|
||||
self.parse_signature(node)
|
||||
node.body = self.parse_statements(('name:endmacro',),
|
||||
drop_needle=True)
|
||||
return node
|
||||
|
||||
def parse_print(self):
|
||||
node = nodes.Output(lineno=next(self.stream).lineno)
|
||||
node.nodes = []
|
||||
while self.stream.current.type != 'block_end':
|
||||
if node.nodes:
|
||||
self.stream.expect('comma')
|
||||
node.nodes.append(self.parse_expression())
|
||||
return node
|
||||
|
||||
def parse_assign_target(self, with_tuple=True, name_only=False,
|
||||
extra_end_rules=None):
|
||||
"""Parse an assignment target. As Jinja2 allows assignments to
|
||||
tuples, this function can parse all allowed assignment targets. Per
|
||||
default assignments to tuples are parsed, that can be disable however
|
||||
by setting `with_tuple` to `False`. If only assignments to names are
|
||||
wanted `name_only` can be set to `True`. The `extra_end_rules`
|
||||
parameter is forwarded to the tuple parsing function.
|
||||
"""
|
||||
if name_only:
|
||||
token = self.stream.expect('name')
|
||||
target = nodes.Name(token.value, 'store', lineno=token.lineno)
|
||||
else:
|
||||
if with_tuple:
|
||||
target = self.parse_tuple(simplified=True,
|
||||
extra_end_rules=extra_end_rules)
|
||||
else:
|
||||
target = self.parse_primary()
|
||||
target.set_ctx('store')
|
||||
if not target.can_assign():
|
||||
self.fail('can\'t assign to %r' % target.__class__.
|
||||
__name__.lower(), target.lineno)
|
||||
return target
|
||||
|
||||
def parse_expression(self, with_condexpr=True):
|
||||
"""Parse an expression. Per default all expressions are parsed, if
|
||||
the optional `with_condexpr` parameter is set to `False` conditional
|
||||
expressions are not parsed.
|
||||
"""
|
||||
if with_condexpr:
|
||||
return self.parse_condexpr()
|
||||
return self.parse_or()
|
||||
|
||||
def parse_condexpr(self):
|
||||
lineno = self.stream.current.lineno
|
||||
expr1 = self.parse_or()
|
||||
while self.stream.skip_if('name:if'):
|
||||
expr2 = self.parse_or()
|
||||
if self.stream.skip_if('name:else'):
|
||||
expr3 = self.parse_condexpr()
|
||||
else:
|
||||
expr3 = None
|
||||
expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return expr1
|
||||
|
||||
def parse_or(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_and()
|
||||
while self.stream.skip_if('name:or'):
|
||||
right = self.parse_and()
|
||||
left = nodes.Or(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_and(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_not()
|
||||
while self.stream.skip_if('name:and'):
|
||||
right = self.parse_not()
|
||||
left = nodes.And(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_not(self):
|
||||
if self.stream.current.test('name:not'):
|
||||
lineno = next(self.stream).lineno
|
||||
return nodes.Not(self.parse_not(), lineno=lineno)
|
||||
return self.parse_compare()
|
||||
|
||||
def parse_compare(self):
|
||||
lineno = self.stream.current.lineno
|
||||
expr = self.parse_add()
|
||||
ops = []
|
||||
while 1:
|
||||
token_type = self.stream.current.type
|
||||
if token_type in _compare_operators:
|
||||
next(self.stream)
|
||||
ops.append(nodes.Operand(token_type, self.parse_add()))
|
||||
elif self.stream.skip_if('name:in'):
|
||||
ops.append(nodes.Operand('in', self.parse_add()))
|
||||
elif self.stream.current.test('name:not') and \
|
||||
self.stream.look().test('name:in'):
|
||||
self.stream.skip(2)
|
||||
ops.append(nodes.Operand('notin', self.parse_add()))
|
||||
else:
|
||||
break
|
||||
lineno = self.stream.current.lineno
|
||||
if not ops:
|
||||
return expr
|
||||
return nodes.Compare(expr, ops, lineno=lineno)
|
||||
|
||||
def parse_add(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_sub()
|
||||
while self.stream.current.type == 'add':
|
||||
next(self.stream)
|
||||
right = self.parse_sub()
|
||||
left = nodes.Add(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_sub(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_concat()
|
||||
while self.stream.current.type == 'sub':
|
||||
next(self.stream)
|
||||
right = self.parse_concat()
|
||||
left = nodes.Sub(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_concat(self):
|
||||
lineno = self.stream.current.lineno
|
||||
args = [self.parse_mul()]
|
||||
while self.stream.current.type == 'tilde':
|
||||
next(self.stream)
|
||||
args.append(self.parse_mul())
|
||||
if len(args) == 1:
|
||||
return args[0]
|
||||
return nodes.Concat(args, lineno=lineno)
|
||||
|
||||
def parse_mul(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_div()
|
||||
while self.stream.current.type == 'mul':
|
||||
next(self.stream)
|
||||
right = self.parse_div()
|
||||
left = nodes.Mul(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_div(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_floordiv()
|
||||
while self.stream.current.type == 'div':
|
||||
next(self.stream)
|
||||
right = self.parse_floordiv()
|
||||
left = nodes.Div(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_floordiv(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_mod()
|
||||
while self.stream.current.type == 'floordiv':
|
||||
next(self.stream)
|
||||
right = self.parse_mod()
|
||||
left = nodes.FloorDiv(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_mod(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_pow()
|
||||
while self.stream.current.type == 'mod':
|
||||
next(self.stream)
|
||||
right = self.parse_pow()
|
||||
left = nodes.Mod(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_pow(self):
|
||||
lineno = self.stream.current.lineno
|
||||
left = self.parse_unary()
|
||||
while self.stream.current.type == 'pow':
|
||||
next(self.stream)
|
||||
right = self.parse_unary()
|
||||
left = nodes.Pow(left, right, lineno=lineno)
|
||||
lineno = self.stream.current.lineno
|
||||
return left
|
||||
|
||||
def parse_unary(self, with_filter=True):
|
||||
token_type = self.stream.current.type
|
||||
lineno = self.stream.current.lineno
|
||||
if token_type == 'sub':
|
||||
next(self.stream)
|
||||
node = nodes.Neg(self.parse_unary(False), lineno=lineno)
|
||||
elif token_type == 'add':
|
||||
next(self.stream)
|
||||
node = nodes.Pos(self.parse_unary(False), lineno=lineno)
|
||||
else:
|
||||
node = self.parse_primary()
|
||||
node = self.parse_postfix(node)
|
||||
if with_filter:
|
||||
node = self.parse_filter_expr(node)
|
||||
return node
|
||||
|
||||
def parse_primary(self):
|
||||
token = self.stream.current
|
||||
if token.type == 'name':
|
||||
if token.value in ('true', 'false', 'True', 'False'):
|
||||
node = nodes.Const(token.value in ('true', 'True'),
|
||||
lineno=token.lineno)
|
||||
elif token.value in ('none', 'None'):
|
||||
node = nodes.Const(None, lineno=token.lineno)
|
||||
else:
|
||||
node = nodes.Name(token.value, 'load', lineno=token.lineno)
|
||||
next(self.stream)
|
||||
elif token.type == 'string':
|
||||
next(self.stream)
|
||||
buf = [token.value]
|
||||
lineno = token.lineno
|
||||
while self.stream.current.type == 'string':
|
||||
buf.append(self.stream.current.value)
|
||||
next(self.stream)
|
||||
node = nodes.Const(''.join(buf), lineno=lineno)
|
||||
elif token.type in ('integer', 'float'):
|
||||
next(self.stream)
|
||||
node = nodes.Const(token.value, lineno=token.lineno)
|
||||
elif token.type == 'lparen':
|
||||
next(self.stream)
|
||||
node = self.parse_tuple(explicit_parentheses=True)
|
||||
self.stream.expect('rparen')
|
||||
elif token.type == 'lbracket':
|
||||
node = self.parse_list()
|
||||
elif token.type == 'lbrace':
|
||||
node = self.parse_dict()
|
||||
else:
|
||||
self.fail("unexpected '%s'" % describe_token(token), token.lineno)
|
||||
return node
|
||||
|
||||
def parse_tuple(self, simplified=False, with_condexpr=True,
|
||||
extra_end_rules=None, explicit_parentheses=False):
|
||||
"""Works like `parse_expression` but if multiple expressions are
|
||||
delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
|
||||
This method could also return a regular expression instead of a tuple
|
||||
if no commas where found.
|
||||
|
||||
The default parsing mode is a full tuple. If `simplified` is `True`
|
||||
only names and literals are parsed. The `no_condexpr` parameter is
|
||||
forwarded to :meth:`parse_expression`.
|
||||
|
||||
Because tuples do not require delimiters and may end in a bogus comma
|
||||
an extra hint is needed that marks the end of a tuple. For example
|
||||
for loops support tuples between `for` and `in`. In that case the
|
||||
`extra_end_rules` is set to ``['name:in']``.
|
||||
|
||||
`explicit_parentheses` is true if the parsing was triggered by an
|
||||
expression in parentheses. This is used to figure out if an empty
|
||||
tuple is a valid expression or not.
|
||||
"""
|
||||
lineno = self.stream.current.lineno
|
||||
if simplified:
|
||||
parse = self.parse_primary
|
||||
elif with_condexpr:
|
||||
parse = self.parse_expression
|
||||
else:
|
||||
parse = lambda: self.parse_expression(with_condexpr=False)
|
||||
args = []
|
||||
is_tuple = False
|
||||
while 1:
|
||||
if args:
|
||||
self.stream.expect('comma')
|
||||
if self.is_tuple_end(extra_end_rules):
|
||||
break
|
||||
args.append(parse())
|
||||
if self.stream.current.type == 'comma':
|
||||
is_tuple = True
|
||||
else:
|
||||
break
|
||||
lineno = self.stream.current.lineno
|
||||
|
||||
if not is_tuple:
|
||||
if args:
|
||||
return args[0]
|
||||
|
||||
# if we don't have explicit parentheses, an empty tuple is
|
||||
# not a valid expression. This would mean nothing (literally
|
||||
# nothing) in the spot of an expression would be an empty
|
||||
# tuple.
|
||||
if not explicit_parentheses:
|
||||
self.fail('Expected an expression, got \'%s\'' %
|
||||
describe_token(self.stream.current))
|
||||
|
||||
return nodes.Tuple(args, 'load', lineno=lineno)
|
||||
|
||||
def parse_list(self):
|
||||
token = self.stream.expect('lbracket')
|
||||
items = []
|
||||
while self.stream.current.type != 'rbracket':
|
||||
if items:
|
||||
self.stream.expect('comma')
|
||||
if self.stream.current.type == 'rbracket':
|
||||
break
|
||||
items.append(self.parse_expression())
|
||||
self.stream.expect('rbracket')
|
||||
return nodes.List(items, lineno=token.lineno)
|
||||
|
||||
def parse_dict(self):
|
||||
token = self.stream.expect('lbrace')
|
||||
items = []
|
||||
while self.stream.current.type != 'rbrace':
|
||||
if items:
|
||||
self.stream.expect('comma')
|
||||
if self.stream.current.type == 'rbrace':
|
||||
break
|
||||
key = self.parse_expression()
|
||||
self.stream.expect('colon')
|
||||
value = self.parse_expression()
|
||||
items.append(nodes.Pair(key, value, lineno=key.lineno))
|
||||
self.stream.expect('rbrace')
|
||||
return nodes.Dict(items, lineno=token.lineno)
|
||||
|
||||
def parse_postfix(self, node):
|
||||
while 1:
|
||||
token_type = self.stream.current.type
|
||||
if token_type == 'dot' or token_type == 'lbracket':
|
||||
node = self.parse_subscript(node)
|
||||
# calls are valid both after postfix expressions (getattr
|
||||
# and getitem) as well as filters and tests
|
||||
elif token_type == 'lparen':
|
||||
node = self.parse_call(node)
|
||||
else:
|
||||
break
|
||||
return node
|
||||
|
||||
def parse_filter_expr(self, node):
|
||||
while 1:
|
||||
token_type = self.stream.current.type
|
||||
if token_type == 'pipe':
|
||||
node = self.parse_filter(node)
|
||||
elif token_type == 'name' and self.stream.current.value == 'is':
|
||||
node = self.parse_test(node)
|
||||
# calls are valid both after postfix expressions (getattr
|
||||
# and getitem) as well as filters and tests
|
||||
elif token_type == 'lparen':
|
||||
node = self.parse_call(node)
|
||||
else:
|
||||
break
|
||||
return node
|
||||
|
||||
def parse_subscript(self, node):
|
||||
token = next(self.stream)
|
||||
if token.type == 'dot':
|
||||
attr_token = self.stream.current
|
||||
next(self.stream)
|
||||
if attr_token.type == 'name':
|
||||
return nodes.Getattr(node, attr_token.value, 'load',
|
||||
lineno=token.lineno)
|
||||
elif attr_token.type != 'integer':
|
||||
self.fail('expected name or number', attr_token.lineno)
|
||||
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
|
||||
return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
|
||||
if token.type == 'lbracket':
|
||||
args = []
|
||||
while self.stream.current.type != 'rbracket':
|
||||
if args:
|
||||
self.stream.expect('comma')
|
||||
args.append(self.parse_subscribed())
|
||||
self.stream.expect('rbracket')
|
||||
if len(args) == 1:
|
||||
arg = args[0]
|
||||
else:
|
||||
arg = nodes.Tuple(args, 'load', lineno=token.lineno)
|
||||
return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
|
||||
self.fail('expected subscript expression', self.lineno)
|
||||
|
||||
def parse_subscribed(self):
|
||||
lineno = self.stream.current.lineno
|
||||
|
||||
if self.stream.current.type == 'colon':
|
||||
next(self.stream)
|
||||
args = [None]
|
||||
else:
|
||||
node = self.parse_expression()
|
||||
if self.stream.current.type != 'colon':
|
||||
return node
|
||||
next(self.stream)
|
||||
args = [node]
|
||||
|
||||
if self.stream.current.type == 'colon':
|
||||
args.append(None)
|
||||
elif self.stream.current.type not in ('rbracket', 'comma'):
|
||||
args.append(self.parse_expression())
|
||||
else:
|
||||
args.append(None)
|
||||
|
||||
if self.stream.current.type == 'colon':
|
||||
next(self.stream)
|
||||
if self.stream.current.type not in ('rbracket', 'comma'):
|
||||
args.append(self.parse_expression())
|
||||
else:
|
||||
args.append(None)
|
||||
else:
|
||||
args.append(None)
|
||||
|
||||
return nodes.Slice(lineno=lineno, *args)
|
||||
|
||||
def parse_call(self, node):
|
||||
token = self.stream.expect('lparen')
|
||||
args = []
|
||||
kwargs = []
|
||||
dyn_args = dyn_kwargs = None
|
||||
require_comma = False
|
||||
|
||||
def ensure(expr):
|
||||
if not expr:
|
||||
self.fail('invalid syntax for function call expression',
|
||||
token.lineno)
|
||||
|
||||
while self.stream.current.type != 'rparen':
|
||||
if require_comma:
|
||||
self.stream.expect('comma')
|
||||
# support for trailing comma
|
||||
if self.stream.current.type == 'rparen':
|
||||
break
|
||||
if self.stream.current.type == 'mul':
|
||||
ensure(dyn_args is None and dyn_kwargs is None)
|
||||
next(self.stream)
|
||||
dyn_args = self.parse_expression()
|
||||
elif self.stream.current.type == 'pow':
|
||||
ensure(dyn_kwargs is None)
|
||||
next(self.stream)
|
||||
dyn_kwargs = self.parse_expression()
|
||||
else:
|
||||
ensure(dyn_args is None and dyn_kwargs is None)
|
||||
if self.stream.current.type == 'name' and \
|
||||
self.stream.look().type == 'assign':
|
||||
key = self.stream.current.value
|
||||
self.stream.skip(2)
|
||||
value = self.parse_expression()
|
||||
kwargs.append(nodes.Keyword(key, value,
|
||||
lineno=value.lineno))
|
||||
else:
|
||||
ensure(not kwargs)
|
||||
args.append(self.parse_expression())
|
||||
|
||||
require_comma = True
|
||||
self.stream.expect('rparen')
|
||||
|
||||
if node is None:
|
||||
return args, kwargs, dyn_args, dyn_kwargs
|
||||
return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
|
||||
lineno=token.lineno)
|
||||
|
||||
def parse_filter(self, node, start_inline=False):
|
||||
while self.stream.current.type == 'pipe' or start_inline:
|
||||
if not start_inline:
|
||||
next(self.stream)
|
||||
token = self.stream.expect('name')
|
||||
name = token.value
|
||||
while self.stream.current.type == 'dot':
|
||||
next(self.stream)
|
||||
name += '.' + self.stream.expect('name').value
|
||||
if self.stream.current.type == 'lparen':
|
||||
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
|
||||
else:
|
||||
args = []
|
||||
kwargs = []
|
||||
dyn_args = dyn_kwargs = None
|
||||
node = nodes.Filter(node, name, args, kwargs, dyn_args,
|
||||
dyn_kwargs, lineno=token.lineno)
|
||||
start_inline = False
|
||||
return node
|
||||
|
||||
def parse_test(self, node):
|
||||
token = next(self.stream)
|
||||
if self.stream.current.test('name:not'):
|
||||
next(self.stream)
|
||||
negated = True
|
||||
else:
|
||||
negated = False
|
||||
name = self.stream.expect('name').value
|
||||
while self.stream.current.type == 'dot':
|
||||
next(self.stream)
|
||||
name += '.' + self.stream.expect('name').value
|
||||
dyn_args = dyn_kwargs = None
|
||||
kwargs = []
|
||||
if self.stream.current.type == 'lparen':
|
||||
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
|
||||
elif self.stream.current.type in ('name', 'string', 'integer',
|
||||
'float', 'lparen', 'lbracket',
|
||||
'lbrace') and not \
|
||||
self.stream.current.test_any('name:else', 'name:or',
|
||||
'name:and'):
|
||||
if self.stream.current.test('name:is'):
|
||||
self.fail('You cannot chain multiple tests with is')
|
||||
args = [self.parse_expression()]
|
||||
else:
|
||||
args = []
|
||||
node = nodes.Test(node, name, args, kwargs, dyn_args,
|
||||
dyn_kwargs, lineno=token.lineno)
|
||||
if negated:
|
||||
node = nodes.Not(node, lineno=token.lineno)
|
||||
return node
|
||||
|
||||
def subparse(self, end_tokens=None):
|
||||
body = []
|
||||
data_buffer = []
|
||||
add_data = data_buffer.append
|
||||
|
||||
if end_tokens is not None:
|
||||
self._end_token_stack.append(end_tokens)
|
||||
|
||||
def flush_data():
|
||||
if data_buffer:
|
||||
lineno = data_buffer[0].lineno
|
||||
body.append(nodes.Output(data_buffer[:], lineno=lineno))
|
||||
del data_buffer[:]
|
||||
|
||||
try:
|
||||
while self.stream:
|
||||
token = self.stream.current
|
||||
if token.type == 'data':
|
||||
if token.value:
|
||||
add_data(nodes.TemplateData(token.value,
|
||||
lineno=token.lineno))
|
||||
next(self.stream)
|
||||
elif token.type == 'variable_begin':
|
||||
next(self.stream)
|
||||
add_data(self.parse_tuple(with_condexpr=True))
|
||||
self.stream.expect('variable_end')
|
||||
elif token.type == 'block_begin':
|
||||
flush_data()
|
||||
next(self.stream)
|
||||
if end_tokens is not None and \
|
||||
self.stream.current.test_any(*end_tokens):
|
||||
return body
|
||||
rv = self.parse_statement()
|
||||
if isinstance(rv, list):
|
||||
body.extend(rv)
|
||||
else:
|
||||
body.append(rv)
|
||||
self.stream.expect('block_end')
|
||||
else:
|
||||
raise AssertionError('internal parsing error')
|
||||
|
||||
flush_data()
|
||||
finally:
|
||||
if end_tokens is not None:
|
||||
self._end_token_stack.pop()
|
||||
|
||||
return body
|
||||
|
||||
def parse(self):
|
||||
"""Parse the whole template into a `Template` node."""
|
||||
result = nodes.Template(self.subparse(), lineno=1)
|
||||
result.set_environment(self.environment)
|
||||
return result
|
||||
Vendored
-581
@@ -1,581 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.runtime
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Runtime helpers.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from itertools import chain
|
||||
from jinja2.nodes import EvalContext, _context_function_types
|
||||
from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
|
||||
internalcode, object_type_repr
|
||||
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
|
||||
TemplateNotFound
|
||||
from jinja2._compat import next, imap, text_type, iteritems, \
|
||||
implements_iterator, implements_to_string, string_types, PY2
|
||||
|
||||
|
||||
# these variables are exported to the template runtime
|
||||
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
|
||||
'TemplateRuntimeError', 'missing', 'concat', 'escape',
|
||||
'markup_join', 'unicode_join', 'to_string', 'identity',
|
||||
'TemplateNotFound']
|
||||
|
||||
#: the name of the function that is used to convert something into
|
||||
#: a string. We can just use the text type here.
|
||||
to_string = text_type
|
||||
|
||||
#: the identity function. Useful for certain things in the environment
|
||||
identity = lambda x: x
|
||||
|
||||
_last_iteration = object()
|
||||
|
||||
|
||||
def markup_join(seq):
|
||||
"""Concatenation that escapes if necessary and converts to unicode."""
|
||||
buf = []
|
||||
iterator = imap(soft_unicode, seq)
|
||||
for arg in iterator:
|
||||
buf.append(arg)
|
||||
if hasattr(arg, '__html__'):
|
||||
return Markup(u'').join(chain(buf, iterator))
|
||||
return concat(buf)
|
||||
|
||||
|
||||
def unicode_join(seq):
|
||||
"""Simple args to unicode conversion and concatenation."""
|
||||
return concat(imap(text_type, seq))
|
||||
|
||||
|
||||
def new_context(environment, template_name, blocks, vars=None,
|
||||
shared=None, globals=None, locals=None):
|
||||
"""Internal helper to for context creation."""
|
||||
if vars is None:
|
||||
vars = {}
|
||||
if shared:
|
||||
parent = vars
|
||||
else:
|
||||
parent = dict(globals or (), **vars)
|
||||
if locals:
|
||||
# if the parent is shared a copy should be created because
|
||||
# we don't want to modify the dict passed
|
||||
if shared:
|
||||
parent = dict(parent)
|
||||
for key, value in iteritems(locals):
|
||||
if key[:2] == 'l_' and value is not missing:
|
||||
parent[key[2:]] = value
|
||||
return Context(environment, parent, template_name, blocks)
|
||||
|
||||
|
||||
class TemplateReference(object):
|
||||
"""The `self` in templates."""
|
||||
|
||||
def __init__(self, context):
|
||||
self.__context = context
|
||||
|
||||
def __getitem__(self, name):
|
||||
blocks = self.__context.blocks[name]
|
||||
return BlockReference(name, self.__context, blocks, 0)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r>' % (
|
||||
self.__class__.__name__,
|
||||
self.__context.name
|
||||
)
|
||||
|
||||
|
||||
class Context(object):
|
||||
"""The template context holds the variables of a template. It stores the
|
||||
values passed to the template and also the names the template exports.
|
||||
Creating instances is neither supported nor useful as it's created
|
||||
automatically at various stages of the template evaluation and should not
|
||||
be created by hand.
|
||||
|
||||
The context is immutable. Modifications on :attr:`parent` **must not**
|
||||
happen and modifications on :attr:`vars` are allowed from generated
|
||||
template code only. Template filters and global functions marked as
|
||||
:func:`contextfunction`\s get the active context passed as first argument
|
||||
and are allowed to access the context read-only.
|
||||
|
||||
The template context supports read only dict operations (`get`,
|
||||
`keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
|
||||
`__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
|
||||
method that doesn't fail with a `KeyError` but returns an
|
||||
:class:`Undefined` object for missing variables.
|
||||
"""
|
||||
__slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
|
||||
'name', 'blocks', '__weakref__')
|
||||
|
||||
def __init__(self, environment, parent, name, blocks):
|
||||
self.parent = parent
|
||||
self.vars = {}
|
||||
self.environment = environment
|
||||
self.eval_ctx = EvalContext(self.environment, name)
|
||||
self.exported_vars = set()
|
||||
self.name = name
|
||||
|
||||
# create the initial mapping of blocks. Whenever template inheritance
|
||||
# takes place the runtime will update this mapping with the new blocks
|
||||
# from the template.
|
||||
self.blocks = dict((k, [v]) for k, v in iteritems(blocks))
|
||||
|
||||
def super(self, name, current):
|
||||
"""Render a parent block."""
|
||||
try:
|
||||
blocks = self.blocks[name]
|
||||
index = blocks.index(current) + 1
|
||||
blocks[index]
|
||||
except LookupError:
|
||||
return self.environment.undefined('there is no parent block '
|
||||
'called %r.' % name,
|
||||
name='super')
|
||||
return BlockReference(name, self, blocks, index)
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Returns an item from the template context, if it doesn't exist
|
||||
`default` is returned.
|
||||
"""
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def resolve(self, key):
|
||||
"""Looks up a variable like `__getitem__` or `get` but returns an
|
||||
:class:`Undefined` object with the name of the name looked up.
|
||||
"""
|
||||
if key in self.vars:
|
||||
return self.vars[key]
|
||||
if key in self.parent:
|
||||
return self.parent[key]
|
||||
return self.environment.undefined(name=key)
|
||||
|
||||
def get_exported(self):
|
||||
"""Get a new dict with the exported variables."""
|
||||
return dict((k, self.vars[k]) for k in self.exported_vars)
|
||||
|
||||
def get_all(self):
|
||||
"""Return a copy of the complete context as dict including the
|
||||
exported variables.
|
||||
"""
|
||||
return dict(self.parent, **self.vars)
|
||||
|
||||
@internalcode
|
||||
def call(__self, __obj, *args, **kwargs):
|
||||
"""Call the callable with the arguments and keyword arguments
|
||||
provided but inject the active context or environment as first
|
||||
argument if the callable is a :func:`contextfunction` or
|
||||
:func:`environmentfunction`.
|
||||
"""
|
||||
if __debug__:
|
||||
__traceback_hide__ = True
|
||||
|
||||
# Allow callable classes to take a context
|
||||
fn = __obj.__call__
|
||||
for fn_type in ('contextfunction',
|
||||
'evalcontextfunction',
|
||||
'environmentfunction'):
|
||||
if hasattr(fn, fn_type):
|
||||
__obj = fn
|
||||
break
|
||||
|
||||
if isinstance(__obj, _context_function_types):
|
||||
if getattr(__obj, 'contextfunction', 0):
|
||||
args = (__self,) + args
|
||||
elif getattr(__obj, 'evalcontextfunction', 0):
|
||||
args = (__self.eval_ctx,) + args
|
||||
elif getattr(__obj, 'environmentfunction', 0):
|
||||
args = (__self.environment,) + args
|
||||
try:
|
||||
return __obj(*args, **kwargs)
|
||||
except StopIteration:
|
||||
return __self.environment.undefined('value was undefined because '
|
||||
'a callable raised a '
|
||||
'StopIteration exception')
|
||||
|
||||
def derived(self, locals=None):
|
||||
"""Internal helper function to create a derived context."""
|
||||
context = new_context(self.environment, self.name, {},
|
||||
self.parent, True, None, locals)
|
||||
context.vars.update(self.vars)
|
||||
context.eval_ctx = self.eval_ctx
|
||||
context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
|
||||
return context
|
||||
|
||||
def _all(meth):
|
||||
proxy = lambda self: getattr(self.get_all(), meth)()
|
||||
proxy.__doc__ = getattr(dict, meth).__doc__
|
||||
proxy.__name__ = meth
|
||||
return proxy
|
||||
|
||||
keys = _all('keys')
|
||||
values = _all('values')
|
||||
items = _all('items')
|
||||
|
||||
# not available on python 3
|
||||
if PY2:
|
||||
iterkeys = _all('iterkeys')
|
||||
itervalues = _all('itervalues')
|
||||
iteritems = _all('iteritems')
|
||||
del _all
|
||||
|
||||
def __contains__(self, name):
|
||||
return name in self.vars or name in self.parent
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Lookup a variable or raise `KeyError` if the variable is
|
||||
undefined.
|
||||
"""
|
||||
item = self.resolve(key)
|
||||
if isinstance(item, Undefined):
|
||||
raise KeyError(key)
|
||||
return item
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s of %r>' % (
|
||||
self.__class__.__name__,
|
||||
repr(self.get_all()),
|
||||
self.name
|
||||
)
|
||||
|
||||
|
||||
# register the context as mapping if possible
|
||||
try:
|
||||
from collections import Mapping
|
||||
Mapping.register(Context)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class BlockReference(object):
|
||||
"""One block on a template reference."""
|
||||
|
||||
def __init__(self, name, context, stack, depth):
|
||||
self.name = name
|
||||
self._context = context
|
||||
self._stack = stack
|
||||
self._depth = depth
|
||||
|
||||
@property
|
||||
def super(self):
|
||||
"""Super the block."""
|
||||
if self._depth + 1 >= len(self._stack):
|
||||
return self._context.environment. \
|
||||
undefined('there is no parent block called %r.' %
|
||||
self.name, name='super')
|
||||
return BlockReference(self.name, self._context, self._stack,
|
||||
self._depth + 1)
|
||||
|
||||
@internalcode
|
||||
def __call__(self):
|
||||
rv = concat(self._stack[self._depth](self._context))
|
||||
if self._context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
return rv
|
||||
|
||||
|
||||
class LoopContext(object):
|
||||
"""A loop context for dynamic iteration."""
|
||||
|
||||
def __init__(self, iterable, recurse=None, depth0=0):
|
||||
self._iterator = iter(iterable)
|
||||
self._recurse = recurse
|
||||
self._after = self._safe_next()
|
||||
self.index0 = -1
|
||||
self.depth0 = depth0
|
||||
|
||||
# try to get the length of the iterable early. This must be done
|
||||
# here because there are some broken iterators around where there
|
||||
# __len__ is the number of iterations left (i'm looking at your
|
||||
# listreverseiterator!).
|
||||
try:
|
||||
self._length = len(iterable)
|
||||
except (TypeError, AttributeError):
|
||||
self._length = None
|
||||
|
||||
def cycle(self, *args):
|
||||
"""Cycles among the arguments with the current loop index."""
|
||||
if not args:
|
||||
raise TypeError('no items for cycling given')
|
||||
return args[self.index0 % len(args)]
|
||||
|
||||
first = property(lambda x: x.index0 == 0)
|
||||
last = property(lambda x: x._after is _last_iteration)
|
||||
index = property(lambda x: x.index0 + 1)
|
||||
revindex = property(lambda x: x.length - x.index0)
|
||||
revindex0 = property(lambda x: x.length - x.index)
|
||||
depth = property(lambda x: x.depth0 + 1)
|
||||
|
||||
def __len__(self):
|
||||
return self.length
|
||||
|
||||
def __iter__(self):
|
||||
return LoopContextIterator(self)
|
||||
|
||||
def _safe_next(self):
|
||||
try:
|
||||
return next(self._iterator)
|
||||
except StopIteration:
|
||||
return _last_iteration
|
||||
|
||||
@internalcode
|
||||
def loop(self, iterable):
|
||||
if self._recurse is None:
|
||||
raise TypeError('Tried to call non recursive loop. Maybe you '
|
||||
"forgot the 'recursive' modifier.")
|
||||
return self._recurse(iterable, self._recurse, self.depth0 + 1)
|
||||
|
||||
# a nifty trick to enhance the error message if someone tried to call
|
||||
# the the loop without or with too many arguments.
|
||||
__call__ = loop
|
||||
del loop
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
if self._length is None:
|
||||
# if was not possible to get the length of the iterator when
|
||||
# the loop context was created (ie: iterating over a generator)
|
||||
# we have to convert the iterable into a sequence and use the
|
||||
# length of that.
|
||||
iterable = tuple(self._iterator)
|
||||
self._iterator = iter(iterable)
|
||||
self._length = len(iterable) + self.index0 + 1
|
||||
return self._length
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r/%r>' % (
|
||||
self.__class__.__name__,
|
||||
self.index,
|
||||
self.length
|
||||
)
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class LoopContextIterator(object):
|
||||
"""The iterator for a loop context."""
|
||||
__slots__ = ('context',)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
ctx = self.context
|
||||
ctx.index0 += 1
|
||||
if ctx._after is _last_iteration:
|
||||
raise StopIteration()
|
||||
next_elem = ctx._after
|
||||
ctx._after = ctx._safe_next()
|
||||
return next_elem, ctx
|
||||
|
||||
|
||||
class Macro(object):
|
||||
"""Wraps a macro function."""
|
||||
|
||||
def __init__(self, environment, func, name, arguments, defaults,
|
||||
catch_kwargs, catch_varargs, caller):
|
||||
self._environment = environment
|
||||
self._func = func
|
||||
self._argument_count = len(arguments)
|
||||
self.name = name
|
||||
self.arguments = arguments
|
||||
self.defaults = defaults
|
||||
self.catch_kwargs = catch_kwargs
|
||||
self.catch_varargs = catch_varargs
|
||||
self.caller = caller
|
||||
|
||||
@internalcode
|
||||
def __call__(self, *args, **kwargs):
|
||||
# try to consume the positional arguments
|
||||
arguments = list(args[:self._argument_count])
|
||||
off = len(arguments)
|
||||
|
||||
# if the number of arguments consumed is not the number of
|
||||
# arguments expected we start filling in keyword arguments
|
||||
# and defaults.
|
||||
if off != self._argument_count:
|
||||
for idx, name in enumerate(self.arguments[len(arguments):]):
|
||||
try:
|
||||
value = kwargs.pop(name)
|
||||
except KeyError:
|
||||
try:
|
||||
value = self.defaults[idx - self._argument_count + off]
|
||||
except IndexError:
|
||||
value = self._environment.undefined(
|
||||
'parameter %r was not provided' % name, name=name)
|
||||
arguments.append(value)
|
||||
|
||||
# it's important that the order of these arguments does not change
|
||||
# if not also changed in the compiler's `function_scoping` method.
|
||||
# the order is caller, keyword arguments, positional arguments!
|
||||
if self.caller:
|
||||
caller = kwargs.pop('caller', None)
|
||||
if caller is None:
|
||||
caller = self._environment.undefined('No caller defined',
|
||||
name='caller')
|
||||
arguments.append(caller)
|
||||
if self.catch_kwargs:
|
||||
arguments.append(kwargs)
|
||||
elif kwargs:
|
||||
raise TypeError('macro %r takes no keyword argument %r' %
|
||||
(self.name, next(iter(kwargs))))
|
||||
if self.catch_varargs:
|
||||
arguments.append(args[self._argument_count:])
|
||||
elif len(args) > self._argument_count:
|
||||
raise TypeError('macro %r takes not more than %d argument(s)' %
|
||||
(self.name, len(self.arguments)))
|
||||
return self._func(*arguments)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (
|
||||
self.__class__.__name__,
|
||||
self.name is None and 'anonymous' or repr(self.name)
|
||||
)
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class Undefined(object):
|
||||
"""The default undefined type. This undefined type can be printed and
|
||||
iterated over, but every other access will raise an :exc:`UndefinedError`:
|
||||
|
||||
>>> foo = Undefined(name='foo')
|
||||
>>> str(foo)
|
||||
''
|
||||
>>> not foo
|
||||
True
|
||||
>>> foo + 42
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UndefinedError: 'foo' is undefined
|
||||
"""
|
||||
__slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
|
||||
'_undefined_exception')
|
||||
|
||||
def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
|
||||
self._undefined_hint = hint
|
||||
self._undefined_obj = obj
|
||||
self._undefined_name = name
|
||||
self._undefined_exception = exc
|
||||
|
||||
@internalcode
|
||||
def _fail_with_undefined_error(self, *args, **kwargs):
|
||||
"""Regular callback function for undefined objects that raises an
|
||||
`UndefinedError` on call.
|
||||
"""
|
||||
if self._undefined_hint is None:
|
||||
if self._undefined_obj is missing:
|
||||
hint = '%r is undefined' % self._undefined_name
|
||||
elif not isinstance(self._undefined_name, string_types):
|
||||
hint = '%s has no element %r' % (
|
||||
object_type_repr(self._undefined_obj),
|
||||
self._undefined_name
|
||||
)
|
||||
else:
|
||||
hint = '%r has no attribute %r' % (
|
||||
object_type_repr(self._undefined_obj),
|
||||
self._undefined_name
|
||||
)
|
||||
else:
|
||||
hint = self._undefined_hint
|
||||
raise self._undefined_exception(hint)
|
||||
|
||||
@internalcode
|
||||
def __getattr__(self, name):
|
||||
if name[:2] == '__':
|
||||
raise AttributeError(name)
|
||||
return self._fail_with_undefined_error()
|
||||
|
||||
__add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
|
||||
__truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
|
||||
__mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
|
||||
__getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \
|
||||
__float__ = __complex__ = __pow__ = __rpow__ = \
|
||||
_fail_with_undefined_error
|
||||
|
||||
def __eq__(self, other):
|
||||
return type(self) is type(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return id(type(self))
|
||||
|
||||
def __str__(self):
|
||||
return u''
|
||||
|
||||
def __len__(self):
|
||||
return 0
|
||||
|
||||
def __iter__(self):
|
||||
if 0:
|
||||
yield None
|
||||
|
||||
def __nonzero__(self):
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return 'Undefined'
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class DebugUndefined(Undefined):
|
||||
"""An undefined that returns the debug info when printed.
|
||||
|
||||
>>> foo = DebugUndefined(name='foo')
|
||||
>>> str(foo)
|
||||
'{{ foo }}'
|
||||
>>> not foo
|
||||
True
|
||||
>>> foo + 42
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UndefinedError: 'foo' is undefined
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __str__(self):
|
||||
if self._undefined_hint is None:
|
||||
if self._undefined_obj is missing:
|
||||
return u'{{ %s }}' % self._undefined_name
|
||||
return '{{ no such element: %s[%r] }}' % (
|
||||
object_type_repr(self._undefined_obj),
|
||||
self._undefined_name
|
||||
)
|
||||
return u'{{ undefined value printed: %s }}' % self._undefined_hint
|
||||
|
||||
|
||||
@implements_to_string
|
||||
class StrictUndefined(Undefined):
|
||||
"""An undefined that barks on print and iteration as well as boolean
|
||||
tests and all kinds of comparisons. In other words: you can do nothing
|
||||
with it except checking if it's defined using the `defined` test.
|
||||
|
||||
>>> foo = StrictUndefined(name='foo')
|
||||
>>> str(foo)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UndefinedError: 'foo' is undefined
|
||||
>>> not foo
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UndefinedError: 'foo' is undefined
|
||||
>>> foo + 42
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UndefinedError: 'foo' is undefined
|
||||
"""
|
||||
__slots__ = ()
|
||||
__iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
|
||||
__ne__ = __bool__ = __hash__ = \
|
||||
Undefined._fail_with_undefined_error
|
||||
|
||||
|
||||
# remove remaining slots attributes, after the metaclass did the magic they
|
||||
# are unneeded and irritating as they contain wrong data for the subclasses.
|
||||
del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__
|
||||
Vendored
-368
@@ -1,368 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.sandbox
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Adds a sandbox layer to Jinja as it was the default behavior in the old
|
||||
Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the
|
||||
default behavior is easier to use.
|
||||
|
||||
The behavior can be changed by subclassing the environment.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
import operator
|
||||
from jinja2.environment import Environment
|
||||
from jinja2.exceptions import SecurityError
|
||||
from jinja2._compat import string_types, function_type, method_type, \
|
||||
traceback_type, code_type, frame_type, generator_type, PY2
|
||||
|
||||
|
||||
#: maximum number of items a range may produce
|
||||
MAX_RANGE = 100000
|
||||
|
||||
#: attributes of function objects that are considered unsafe.
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
|
||||
'func_defaults', 'func_globals'])
|
||||
|
||||
#: unsafe method attributes. function attributes are unsafe for methods too
|
||||
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
|
||||
|
||||
#: unsafe generator attirbutes.
|
||||
UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
|
||||
|
||||
# On versions > python 2 the special attributes on functions are gone,
|
||||
# but they remain on methods and generators for whatever reason.
|
||||
if not PY2:
|
||||
UNSAFE_FUNCTION_ATTRIBUTES = set()
|
||||
|
||||
import warnings
|
||||
|
||||
# make sure we don't warn in python 2.6 about stuff we don't care about
|
||||
warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
|
||||
module='jinja2.sandbox')
|
||||
|
||||
from collections import deque
|
||||
|
||||
_mutable_set_types = (set,)
|
||||
_mutable_mapping_types = (dict,)
|
||||
_mutable_sequence_types = (list,)
|
||||
|
||||
|
||||
# on python 2.x we can register the user collection types
|
||||
try:
|
||||
from UserDict import UserDict, DictMixin
|
||||
from UserList import UserList
|
||||
_mutable_mapping_types += (UserDict, DictMixin)
|
||||
_mutable_set_types += (UserList,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# if sets is still available, register the mutable set from there as well
|
||||
try:
|
||||
from sets import Set
|
||||
_mutable_set_types += (Set,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
#: register Python 2.6 abstract base classes
|
||||
try:
|
||||
from collections import MutableSet, MutableMapping, MutableSequence
|
||||
_mutable_set_types += (MutableSet,)
|
||||
_mutable_mapping_types += (MutableMapping,)
|
||||
_mutable_sequence_types += (MutableSequence,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
_mutable_spec = (
|
||||
(_mutable_set_types, frozenset([
|
||||
'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
|
||||
'symmetric_difference_update', 'update'
|
||||
])),
|
||||
(_mutable_mapping_types, frozenset([
|
||||
'clear', 'pop', 'popitem', 'setdefault', 'update'
|
||||
])),
|
||||
(_mutable_sequence_types, frozenset([
|
||||
'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
|
||||
])),
|
||||
(deque, frozenset([
|
||||
'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
|
||||
'popleft', 'remove', 'rotate'
|
||||
]))
|
||||
)
|
||||
|
||||
|
||||
def safe_range(*args):
|
||||
"""A range that can't generate ranges with a length of more than
|
||||
MAX_RANGE items.
|
||||
"""
|
||||
rng = range(*args)
|
||||
if len(rng) > MAX_RANGE:
|
||||
raise OverflowError('range too big, maximum size for range is %d' %
|
||||
MAX_RANGE)
|
||||
return rng
|
||||
|
||||
|
||||
def unsafe(f):
|
||||
"""Marks a function or method as unsafe.
|
||||
|
||||
::
|
||||
|
||||
@unsafe
|
||||
def delete(self):
|
||||
pass
|
||||
"""
|
||||
f.unsafe_callable = True
|
||||
return f
|
||||
|
||||
|
||||
def is_internal_attribute(obj, attr):
|
||||
"""Test if the attribute given is an internal python attribute. For
|
||||
example this function returns `True` for the `func_code` attribute of
|
||||
python objects. This is useful if the environment method
|
||||
:meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
|
||||
|
||||
>>> from jinja2.sandbox import is_internal_attribute
|
||||
>>> is_internal_attribute(lambda: None, "func_code")
|
||||
True
|
||||
>>> is_internal_attribute((lambda x:x).func_code, 'co_code')
|
||||
True
|
||||
>>> is_internal_attribute(str, "upper")
|
||||
False
|
||||
"""
|
||||
if isinstance(obj, function_type):
|
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES:
|
||||
return True
|
||||
elif isinstance(obj, method_type):
|
||||
if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
|
||||
attr in UNSAFE_METHOD_ATTRIBUTES:
|
||||
return True
|
||||
elif isinstance(obj, type):
|
||||
if attr == 'mro':
|
||||
return True
|
||||
elif isinstance(obj, (code_type, traceback_type, frame_type)):
|
||||
return True
|
||||
elif isinstance(obj, generator_type):
|
||||
if attr in UNSAFE_GENERATOR_ATTRIBUTES:
|
||||
return True
|
||||
return attr.startswith('__')
|
||||
|
||||
|
||||
def modifies_known_mutable(obj, attr):
|
||||
"""This function checks if an attribute on a builtin mutable object
|
||||
(list, dict, set or deque) would modify it if called. It also supports
|
||||
the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
|
||||
with Python 2.6 onwards the abstract base classes `MutableSet`,
|
||||
`MutableMapping`, and `MutableSequence`.
|
||||
|
||||
>>> modifies_known_mutable({}, "clear")
|
||||
True
|
||||
>>> modifies_known_mutable({}, "keys")
|
||||
False
|
||||
>>> modifies_known_mutable([], "append")
|
||||
True
|
||||
>>> modifies_known_mutable([], "index")
|
||||
False
|
||||
|
||||
If called with an unsupported object (such as unicode) `False` is
|
||||
returned.
|
||||
|
||||
>>> modifies_known_mutable("foo", "upper")
|
||||
False
|
||||
"""
|
||||
for typespec, unsafe in _mutable_spec:
|
||||
if isinstance(obj, typespec):
|
||||
return attr in unsafe
|
||||
return False
|
||||
|
||||
|
||||
class SandboxedEnvironment(Environment):
|
||||
"""The sandboxed environment. It works like the regular environment but
|
||||
tells the compiler to generate sandboxed code. Additionally subclasses of
|
||||
this environment may override the methods that tell the runtime what
|
||||
attributes or functions are safe to access.
|
||||
|
||||
If the template tries to access insecure code a :exc:`SecurityError` is
|
||||
raised. However also other exceptions may occour during the rendering so
|
||||
the caller has to ensure that all exceptions are catched.
|
||||
"""
|
||||
sandboxed = True
|
||||
|
||||
#: default callback table for the binary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`binop_table`
|
||||
default_binop_table = {
|
||||
'+': operator.add,
|
||||
'-': operator.sub,
|
||||
'*': operator.mul,
|
||||
'/': operator.truediv,
|
||||
'//': operator.floordiv,
|
||||
'**': operator.pow,
|
||||
'%': operator.mod
|
||||
}
|
||||
|
||||
#: default callback table for the unary operators. A copy of this is
|
||||
#: available on each instance of a sandboxed environment as
|
||||
#: :attr:`unop_table`
|
||||
default_unop_table = {
|
||||
'+': operator.pos,
|
||||
'-': operator.neg
|
||||
}
|
||||
|
||||
#: a set of binary operators that should be intercepted. Each operator
|
||||
#: that is added to this set (empty by default) is delegated to the
|
||||
#: :meth:`call_binop` method that will perform the operator. The default
|
||||
#: operator callback is specified by :attr:`binop_table`.
|
||||
#:
|
||||
#: The following binary operators are interceptable:
|
||||
#: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
|
||||
#:
|
||||
#: The default operation form the operator table corresponds to the
|
||||
#: builtin function. Intercepted calls are always slower than the native
|
||||
#: operator call, so make sure only to intercept the ones you are
|
||||
#: interested in.
|
||||
#:
|
||||
#: .. versionadded:: 2.6
|
||||
intercepted_binops = frozenset()
|
||||
|
||||
#: a set of unary operators that should be intercepted. Each operator
|
||||
#: that is added to this set (empty by default) is delegated to the
|
||||
#: :meth:`call_unop` method that will perform the operator. The default
|
||||
#: operator callback is specified by :attr:`unop_table`.
|
||||
#:
|
||||
#: The following unary operators are interceptable: ``+``, ``-``
|
||||
#:
|
||||
#: The default operation form the operator table corresponds to the
|
||||
#: builtin function. Intercepted calls are always slower than the native
|
||||
#: operator call, so make sure only to intercept the ones you are
|
||||
#: interested in.
|
||||
#:
|
||||
#: .. versionadded:: 2.6
|
||||
intercepted_unops = frozenset()
|
||||
|
||||
def intercept_unop(self, operator):
|
||||
"""Called during template compilation with the name of a unary
|
||||
operator to check if it should be intercepted at runtime. If this
|
||||
method returns `True`, :meth:`call_unop` is excuted for this unary
|
||||
operator. The default implementation of :meth:`call_unop` will use
|
||||
the :attr:`unop_table` dictionary to perform the operator with the
|
||||
same logic as the builtin one.
|
||||
|
||||
The following unary operators are interceptable: ``+`` and ``-``
|
||||
|
||||
Intercepted calls are always slower than the native operator call,
|
||||
so make sure only to intercept the ones you are interested in.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
Environment.__init__(self, *args, **kwargs)
|
||||
self.globals['range'] = safe_range
|
||||
self.binop_table = self.default_binop_table.copy()
|
||||
self.unop_table = self.default_unop_table.copy()
|
||||
|
||||
def is_safe_attribute(self, obj, attr, value):
|
||||
"""The sandboxed environment will call this method to check if the
|
||||
attribute of an object is safe to access. Per default all attributes
|
||||
starting with an underscore are considered private as well as the
|
||||
special attributes of internal python objects as returned by the
|
||||
:func:`is_internal_attribute` function.
|
||||
"""
|
||||
return not (attr.startswith('_') or is_internal_attribute(obj, attr))
|
||||
|
||||
def is_safe_callable(self, obj):
|
||||
"""Check if an object is safely callable. Per default a function is
|
||||
considered safe unless the `unsafe_callable` attribute exists and is
|
||||
True. Override this method to alter the behavior, but this won't
|
||||
affect the `unsafe` decorator from this module.
|
||||
"""
|
||||
return not (getattr(obj, 'unsafe_callable', False) or
|
||||
getattr(obj, 'alters_data', False))
|
||||
|
||||
def call_binop(self, context, operator, left, right):
|
||||
"""For intercepted binary operator calls (:meth:`intercepted_binops`)
|
||||
this function is executed instead of the builtin operator. This can
|
||||
be used to fine tune the behavior of certain operators.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return self.binop_table[operator](left, right)
|
||||
|
||||
def call_unop(self, context, operator, arg):
|
||||
"""For intercepted unary operator calls (:meth:`intercepted_unops`)
|
||||
this function is executed instead of the builtin operator. This can
|
||||
be used to fine tune the behavior of certain operators.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return self.unop_table[operator](arg)
|
||||
|
||||
def getitem(self, obj, argument):
|
||||
"""Subscribe an object from sandboxed code."""
|
||||
try:
|
||||
return obj[argument]
|
||||
except (TypeError, LookupError):
|
||||
if isinstance(argument, string_types):
|
||||
try:
|
||||
attr = str(argument)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
value = getattr(obj, attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if self.is_safe_attribute(obj, argument, value):
|
||||
return value
|
||||
return self.unsafe_undefined(obj, argument)
|
||||
return self.undefined(obj=obj, name=argument)
|
||||
|
||||
def getattr(self, obj, attribute):
|
||||
"""Subscribe an object from sandboxed code and prefer the
|
||||
attribute. The attribute passed *must* be a bytestring.
|
||||
"""
|
||||
try:
|
||||
value = getattr(obj, attribute)
|
||||
except AttributeError:
|
||||
try:
|
||||
return obj[attribute]
|
||||
except (TypeError, LookupError):
|
||||
pass
|
||||
else:
|
||||
if self.is_safe_attribute(obj, attribute, value):
|
||||
return value
|
||||
return self.unsafe_undefined(obj, attribute)
|
||||
return self.undefined(obj=obj, name=attribute)
|
||||
|
||||
def unsafe_undefined(self, obj, attribute):
|
||||
"""Return an undefined object for unsafe attributes."""
|
||||
return self.undefined('access to attribute %r of %r '
|
||||
'object is unsafe.' % (
|
||||
attribute,
|
||||
obj.__class__.__name__
|
||||
), name=attribute, obj=obj, exc=SecurityError)
|
||||
|
||||
def call(__self, __context, __obj, *args, **kwargs):
|
||||
"""Call an object from sandboxed code."""
|
||||
# the double prefixes are to avoid double keyword argument
|
||||
# errors when proxying the call.
|
||||
if not __self.is_safe_callable(__obj):
|
||||
raise SecurityError('%r is not safely callable' % (__obj,))
|
||||
return __context.call(__obj, *args, **kwargs)
|
||||
|
||||
|
||||
class ImmutableSandboxedEnvironment(SandboxedEnvironment):
|
||||
"""Works exactly like the regular `SandboxedEnvironment` but does not
|
||||
permit modifications on the builtin mutable objects `list`, `set`, and
|
||||
`dict` by using the :func:`modifies_known_mutable` function.
|
||||
"""
|
||||
|
||||
def is_safe_attribute(self, obj, attr, value):
|
||||
if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
|
||||
return False
|
||||
return not modifies_known_mutable(obj, attr)
|
||||
Vendored
-149
@@ -1,149 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.tests
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Jinja test functions. Used with the "is" operator.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
from jinja2.runtime import Undefined
|
||||
from jinja2._compat import text_type, string_types, mapping_types
|
||||
|
||||
|
||||
number_re = re.compile(r'^-?\d+(\.\d+)?$')
|
||||
regex_type = type(number_re)
|
||||
|
||||
|
||||
test_callable = callable
|
||||
|
||||
|
||||
def test_odd(value):
|
||||
"""Return true if the variable is odd."""
|
||||
return value % 2 == 1
|
||||
|
||||
|
||||
def test_even(value):
|
||||
"""Return true if the variable is even."""
|
||||
return value % 2 == 0
|
||||
|
||||
|
||||
def test_divisibleby(value, num):
|
||||
"""Check if a variable is divisible by a number."""
|
||||
return value % num == 0
|
||||
|
||||
|
||||
def test_defined(value):
|
||||
"""Return true if the variable is defined:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% if variable is defined %}
|
||||
value of variable: {{ variable }}
|
||||
{% else %}
|
||||
variable is not defined
|
||||
{% endif %}
|
||||
|
||||
See the :func:`default` filter for a simple way to set undefined
|
||||
variables.
|
||||
"""
|
||||
return not isinstance(value, Undefined)
|
||||
|
||||
|
||||
def test_undefined(value):
|
||||
"""Like :func:`defined` but the other way round."""
|
||||
return isinstance(value, Undefined)
|
||||
|
||||
|
||||
def test_none(value):
|
||||
"""Return true if the variable is none."""
|
||||
return value is None
|
||||
|
||||
|
||||
def test_lower(value):
|
||||
"""Return true if the variable is lowercased."""
|
||||
return text_type(value).islower()
|
||||
|
||||
|
||||
def test_upper(value):
|
||||
"""Return true if the variable is uppercased."""
|
||||
return text_type(value).isupper()
|
||||
|
||||
|
||||
def test_string(value):
|
||||
"""Return true if the object is a string."""
|
||||
return isinstance(value, string_types)
|
||||
|
||||
|
||||
def test_mapping(value):
|
||||
"""Return true if the object is a mapping (dict etc.).
|
||||
|
||||
.. versionadded:: 2.6
|
||||
"""
|
||||
return isinstance(value, mapping_types)
|
||||
|
||||
|
||||
def test_number(value):
|
||||
"""Return true if the variable is a number."""
|
||||
return isinstance(value, (int, float, complex))
|
||||
|
||||
|
||||
def test_sequence(value):
|
||||
"""Return true if the variable is a sequence. Sequences are variables
|
||||
that are iterable.
|
||||
"""
|
||||
try:
|
||||
len(value)
|
||||
value.__getitem__
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def test_sameas(value, other):
|
||||
"""Check if an object points to the same memory address than another
|
||||
object:
|
||||
|
||||
.. sourcecode:: jinja
|
||||
|
||||
{% if foo.attribute is sameas false %}
|
||||
the foo attribute really is the `False` singleton
|
||||
{% endif %}
|
||||
"""
|
||||
return value is other
|
||||
|
||||
|
||||
def test_iterable(value):
|
||||
"""Check if it's possible to iterate over an object."""
|
||||
try:
|
||||
iter(value)
|
||||
except TypeError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def test_escaped(value):
|
||||
"""Check if the value is escaped."""
|
||||
return hasattr(value, '__html__')
|
||||
|
||||
|
||||
TESTS = {
|
||||
'odd': test_odd,
|
||||
'even': test_even,
|
||||
'divisibleby': test_divisibleby,
|
||||
'defined': test_defined,
|
||||
'undefined': test_undefined,
|
||||
'none': test_none,
|
||||
'lower': test_lower,
|
||||
'upper': test_upper,
|
||||
'string': test_string,
|
||||
'mapping': test_mapping,
|
||||
'number': test_number,
|
||||
'sequence': test_sequence,
|
||||
'iterable': test_iterable,
|
||||
'callable': test_callable,
|
||||
'sameas': test_sameas,
|
||||
'escaped': test_escaped
|
||||
}
|
||||
Vendored
-520
@@ -1,520 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.utils
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Utility functions.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
import errno
|
||||
from collections import deque
|
||||
from jinja2._compat import text_type, string_types, implements_iterator, \
|
||||
allocate_lock, url_quote
|
||||
|
||||
|
||||
_word_split_re = re.compile(r'(\s+)')
|
||||
_punctuation_re = re.compile(
|
||||
'^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
|
||||
'|'.join(map(re.escape, ('(', '<', '<'))),
|
||||
'|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>')))
|
||||
)
|
||||
)
|
||||
_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
|
||||
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
||||
_entity_re = re.compile(r'&([^;]+);')
|
||||
_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
_digits = '0123456789'
|
||||
|
||||
# special singleton representing missing values for the runtime
|
||||
missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
|
||||
|
||||
# internal code
|
||||
internal_code = set()
|
||||
|
||||
concat = u''.join
|
||||
|
||||
|
||||
def contextfunction(f):
|
||||
"""This decorator can be used to mark a function or method context callable.
|
||||
A context callable is passed the active :class:`Context` as first argument when
|
||||
called from the template. This is useful if a function wants to get access
|
||||
to the context or functions provided on the context object. For example
|
||||
a function that returns a sorted list of template variables the current
|
||||
template exports could look like this::
|
||||
|
||||
@contextfunction
|
||||
def get_exported_names(context):
|
||||
return sorted(context.exported_vars)
|
||||
"""
|
||||
f.contextfunction = True
|
||||
return f
|
||||
|
||||
|
||||
def evalcontextfunction(f):
|
||||
"""This decorator can be used to mark a function or method as an eval
|
||||
context callable. This is similar to the :func:`contextfunction`
|
||||
but instead of passing the context, an evaluation context object is
|
||||
passed. For more information about the eval context, see
|
||||
:ref:`eval-context`.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
"""
|
||||
f.evalcontextfunction = True
|
||||
return f
|
||||
|
||||
|
||||
def environmentfunction(f):
|
||||
"""This decorator can be used to mark a function or method as environment
|
||||
callable. This decorator works exactly like the :func:`contextfunction`
|
||||
decorator just that the first argument is the active :class:`Environment`
|
||||
and not context.
|
||||
"""
|
||||
f.environmentfunction = True
|
||||
return f
|
||||
|
||||
|
||||
def internalcode(f):
|
||||
"""Marks the function as internally used"""
|
||||
internal_code.add(f.__code__)
|
||||
return f
|
||||
|
||||
|
||||
def is_undefined(obj):
|
||||
"""Check if the object passed is undefined. This does nothing more than
|
||||
performing an instance check against :class:`Undefined` but looks nicer.
|
||||
This can be used for custom filters or tests that want to react to
|
||||
undefined variables. For example a custom default filter can look like
|
||||
this::
|
||||
|
||||
def default(var, default=''):
|
||||
if is_undefined(var):
|
||||
return default
|
||||
return var
|
||||
"""
|
||||
from jinja2.runtime import Undefined
|
||||
return isinstance(obj, Undefined)
|
||||
|
||||
|
||||
def consume(iterable):
|
||||
"""Consumes an iterable without doing anything with it."""
|
||||
for event in iterable:
|
||||
pass
|
||||
|
||||
|
||||
def clear_caches():
|
||||
"""Jinja2 keeps internal caches for environments and lexers. These are
|
||||
used so that Jinja2 doesn't have to recreate environments and lexers all
|
||||
the time. Normally you don't have to care about that but if you are
|
||||
messuring memory consumption you may want to clean the caches.
|
||||
"""
|
||||
from jinja2.environment import _spontaneous_environments
|
||||
from jinja2.lexer import _lexer_cache
|
||||
_spontaneous_environments.clear()
|
||||
_lexer_cache.clear()
|
||||
|
||||
|
||||
def import_string(import_name, silent=False):
|
||||
"""Imports an object based on a string. This is useful if you want to
|
||||
use import paths as endpoints or something similar. An import path can
|
||||
be specified either in dotted notation (``xml.sax.saxutils.escape``)
|
||||
or with a colon as object delimiter (``xml.sax.saxutils:escape``).
|
||||
|
||||
If the `silent` is True the return value will be `None` if the import
|
||||
fails.
|
||||
|
||||
:return: imported object
|
||||
"""
|
||||
try:
|
||||
if ':' in import_name:
|
||||
module, obj = import_name.split(':', 1)
|
||||
elif '.' in import_name:
|
||||
items = import_name.split('.')
|
||||
module = '.'.join(items[:-1])
|
||||
obj = items[-1]
|
||||
else:
|
||||
return __import__(import_name)
|
||||
return getattr(__import__(module, None, None, [obj]), obj)
|
||||
except (ImportError, AttributeError):
|
||||
if not silent:
|
||||
raise
|
||||
|
||||
|
||||
def open_if_exists(filename, mode='rb'):
|
||||
"""Returns a file descriptor for the filename if that file exists,
|
||||
otherwise `None`.
|
||||
"""
|
||||
try:
|
||||
return open(filename, mode)
|
||||
except IOError as e:
|
||||
if e.errno not in (errno.ENOENT, errno.EISDIR):
|
||||
raise
|
||||
|
||||
|
||||
def object_type_repr(obj):
|
||||
"""Returns the name of the object's type. For some recognized
|
||||
singletons the name of the object is returned instead. (For
|
||||
example for `None` and `Ellipsis`).
|
||||
"""
|
||||
if obj is None:
|
||||
return 'None'
|
||||
elif obj is Ellipsis:
|
||||
return 'Ellipsis'
|
||||
# __builtin__ in 2.x, builtins in 3.x
|
||||
if obj.__class__.__module__ in ('__builtin__', 'builtins'):
|
||||
name = obj.__class__.__name__
|
||||
else:
|
||||
name = obj.__class__.__module__ + '.' + obj.__class__.__name__
|
||||
return '%s object' % name
|
||||
|
||||
|
||||
def pformat(obj, verbose=False):
|
||||
"""Prettyprint an object. Either use the `pretty` library or the
|
||||
builtin `pprint`.
|
||||
"""
|
||||
try:
|
||||
from pretty import pretty
|
||||
return pretty(obj, verbose=verbose)
|
||||
except ImportError:
|
||||
from pprint import pformat
|
||||
return pformat(obj)
|
||||
|
||||
|
||||
def urlize(text, trim_url_limit=None, nofollow=False):
|
||||
"""Converts any URLs in text into clickable links. Works on http://,
|
||||
https:// and www. links. Links can have trailing punctuation (periods,
|
||||
commas, close-parens) and leading punctuation (opening parens) and
|
||||
it'll still do the right thing.
|
||||
|
||||
If trim_url_limit is not None, the URLs in link text will be limited
|
||||
to trim_url_limit characters.
|
||||
|
||||
If nofollow is True, the URLs in link text will get a rel="nofollow"
|
||||
attribute.
|
||||
"""
|
||||
trim_url = lambda x, limit=trim_url_limit: limit is not None \
|
||||
and (x[:limit] + (len(x) >=limit and '...'
|
||||
or '')) or x
|
||||
words = _word_split_re.split(text_type(escape(text)))
|
||||
nofollow_attr = nofollow and ' rel="nofollow"' or ''
|
||||
for i, word in enumerate(words):
|
||||
match = _punctuation_re.match(word)
|
||||
if match:
|
||||
lead, middle, trail = match.groups()
|
||||
if middle.startswith('www.') or (
|
||||
'@' not in middle and
|
||||
not middle.startswith('http://') and
|
||||
not middle.startswith('https://') and
|
||||
len(middle) > 0 and
|
||||
middle[0] in _letters + _digits and (
|
||||
middle.endswith('.org') or
|
||||
middle.endswith('.net') or
|
||||
middle.endswith('.com')
|
||||
)):
|
||||
middle = '<a href="http://%s"%s>%s</a>' % (middle,
|
||||
nofollow_attr, trim_url(middle))
|
||||
if middle.startswith('http://') or \
|
||||
middle.startswith('https://'):
|
||||
middle = '<a href="%s"%s>%s</a>' % (middle,
|
||||
nofollow_attr, trim_url(middle))
|
||||
if '@' in middle and not middle.startswith('www.') and \
|
||||
not ':' in middle and _simple_email_re.match(middle):
|
||||
middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
|
||||
if lead + middle + trail != word:
|
||||
words[i] = lead + middle + trail
|
||||
return u''.join(words)
|
||||
|
||||
|
||||
def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
||||
"""Generate some lorem impsum for the template."""
|
||||
from jinja2.constants import LOREM_IPSUM_WORDS
|
||||
from random import choice, randrange
|
||||
words = LOREM_IPSUM_WORDS.split()
|
||||
result = []
|
||||
|
||||
for _ in range(n):
|
||||
next_capitalized = True
|
||||
last_comma = last_fullstop = 0
|
||||
word = None
|
||||
last = None
|
||||
p = []
|
||||
|
||||
# each paragraph contains out of 20 to 100 words.
|
||||
for idx, _ in enumerate(range(randrange(min, max))):
|
||||
while True:
|
||||
word = choice(words)
|
||||
if word != last:
|
||||
last = word
|
||||
break
|
||||
if next_capitalized:
|
||||
word = word.capitalize()
|
||||
next_capitalized = False
|
||||
# add commas
|
||||
if idx - randrange(3, 8) > last_comma:
|
||||
last_comma = idx
|
||||
last_fullstop += 2
|
||||
word += ','
|
||||
# add end of sentences
|
||||
if idx - randrange(10, 20) > last_fullstop:
|
||||
last_comma = last_fullstop = idx
|
||||
word += '.'
|
||||
next_capitalized = True
|
||||
p.append(word)
|
||||
|
||||
# ensure that the paragraph ends with a dot.
|
||||
p = u' '.join(p)
|
||||
if p.endswith(','):
|
||||
p = p[:-1] + '.'
|
||||
elif not p.endswith('.'):
|
||||
p += '.'
|
||||
result.append(p)
|
||||
|
||||
if not html:
|
||||
return u'\n\n'.join(result)
|
||||
return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
|
||||
|
||||
|
||||
def unicode_urlencode(obj, charset='utf-8'):
|
||||
"""URL escapes a single bytestring or unicode string with the
|
||||
given charset if applicable to URL safe quoting under all rules
|
||||
that need to be considered under all supported Python versions.
|
||||
|
||||
If non strings are provided they are converted to their unicode
|
||||
representation first.
|
||||
"""
|
||||
if not isinstance(obj, string_types):
|
||||
obj = text_type(obj)
|
||||
if isinstance(obj, text_type):
|
||||
obj = obj.encode(charset)
|
||||
return text_type(url_quote(obj))
|
||||
|
||||
|
||||
class LRUCache(object):
|
||||
"""A simple LRU Cache implementation."""
|
||||
|
||||
# this is fast for small capacities (something below 1000) but doesn't
|
||||
# scale. But as long as it's only used as storage for templates this
|
||||
# won't do any harm.
|
||||
|
||||
def __init__(self, capacity):
|
||||
self.capacity = capacity
|
||||
self._mapping = {}
|
||||
self._queue = deque()
|
||||
self._postinit()
|
||||
|
||||
def _postinit(self):
|
||||
# alias all queue methods for faster lookup
|
||||
self._popleft = self._queue.popleft
|
||||
self._pop = self._queue.pop
|
||||
self._remove = self._queue.remove
|
||||
self._wlock = allocate_lock()
|
||||
self._append = self._queue.append
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
'capacity': self.capacity,
|
||||
'_mapping': self._mapping,
|
||||
'_queue': self._queue
|
||||
}
|
||||
|
||||
def __setstate__(self, d):
|
||||
self.__dict__.update(d)
|
||||
self._postinit()
|
||||
|
||||
def __getnewargs__(self):
|
||||
return (self.capacity,)
|
||||
|
||||
def copy(self):
|
||||
"""Return a shallow copy of the instance."""
|
||||
rv = self.__class__(self.capacity)
|
||||
rv._mapping.update(self._mapping)
|
||||
rv._queue = deque(self._queue)
|
||||
return rv
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Return an item from the cache dict or `default`"""
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
"""Set `default` if the key is not in the cache otherwise
|
||||
leave unchanged. Return the value of this key.
|
||||
"""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
finally:
|
||||
self._wlock.release()
|
||||
|
||||
def clear(self):
|
||||
"""Clear the cache."""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
self._mapping.clear()
|
||||
self._queue.clear()
|
||||
finally:
|
||||
self._wlock.release()
|
||||
|
||||
def __contains__(self, key):
|
||||
"""Check if a key exists in this cache."""
|
||||
return key in self._mapping
|
||||
|
||||
def __len__(self):
|
||||
"""Return the current size of the cache."""
|
||||
return len(self._mapping)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r>' % (
|
||||
self.__class__.__name__,
|
||||
self._mapping
|
||||
)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get an item from the cache. Moves the item up so that it has the
|
||||
highest priority then.
|
||||
|
||||
Raise a `KeyError` if it does not exist.
|
||||
"""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
rv = self._mapping[key]
|
||||
if self._queue[-1] != key:
|
||||
try:
|
||||
self._remove(key)
|
||||
except ValueError:
|
||||
# if something removed the key from the container
|
||||
# when we read, ignore the ValueError that we would
|
||||
# get otherwise.
|
||||
pass
|
||||
self._append(key)
|
||||
return rv
|
||||
finally:
|
||||
self._wlock.release()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Sets the value for an item. Moves the item up so that it
|
||||
has the highest priority then.
|
||||
"""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
if key in self._mapping:
|
||||
self._remove(key)
|
||||
elif len(self._mapping) == self.capacity:
|
||||
del self._mapping[self._popleft()]
|
||||
self._append(key)
|
||||
self._mapping[key] = value
|
||||
finally:
|
||||
self._wlock.release()
|
||||
|
||||
def __delitem__(self, key):
|
||||
"""Remove an item from the cache dict.
|
||||
Raise a `KeyError` if it does not exist.
|
||||
"""
|
||||
self._wlock.acquire()
|
||||
try:
|
||||
del self._mapping[key]
|
||||
try:
|
||||
self._remove(key)
|
||||
except ValueError:
|
||||
# __getitem__ is not locked, it might happen
|
||||
pass
|
||||
finally:
|
||||
self._wlock.release()
|
||||
|
||||
def items(self):
|
||||
"""Return a list of items."""
|
||||
result = [(key, self._mapping[key]) for key in list(self._queue)]
|
||||
result.reverse()
|
||||
return result
|
||||
|
||||
def iteritems(self):
|
||||
"""Iterate over all items."""
|
||||
return iter(self.items())
|
||||
|
||||
def values(self):
|
||||
"""Return a list of all values."""
|
||||
return [x[1] for x in self.items()]
|
||||
|
||||
def itervalue(self):
|
||||
"""Iterate over all values."""
|
||||
return iter(self.values())
|
||||
|
||||
def keys(self):
|
||||
"""Return a list of all keys ordered by most recent usage."""
|
||||
return list(self)
|
||||
|
||||
def iterkeys(self):
|
||||
"""Iterate over all keys in the cache dict, ordered by
|
||||
the most recent usage.
|
||||
"""
|
||||
return reversed(tuple(self._queue))
|
||||
|
||||
__iter__ = iterkeys
|
||||
|
||||
def __reversed__(self):
|
||||
"""Iterate over the values in the cache dict, oldest items
|
||||
coming first.
|
||||
"""
|
||||
return iter(tuple(self._queue))
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
|
||||
# register the LRU cache as mutable mapping if possible
|
||||
try:
|
||||
from collections import MutableMapping
|
||||
MutableMapping.register(LRUCache)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
@implements_iterator
|
||||
class Cycler(object):
|
||||
"""A cycle helper for templates."""
|
||||
|
||||
def __init__(self, *items):
|
||||
if not items:
|
||||
raise RuntimeError('at least one item has to be provided')
|
||||
self.items = items
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Resets the cycle."""
|
||||
self.pos = 0
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
"""Returns the current item."""
|
||||
return self.items[self.pos]
|
||||
|
||||
def __next__(self):
|
||||
"""Goes one item ahead and returns it."""
|
||||
rv = self.current
|
||||
self.pos = (self.pos + 1) % len(self.items)
|
||||
return rv
|
||||
|
||||
|
||||
class Joiner(object):
|
||||
"""A joining helper for templates."""
|
||||
|
||||
def __init__(self, sep=u', '):
|
||||
self.sep = sep
|
||||
self.used = False
|
||||
|
||||
def __call__(self):
|
||||
if not self.used:
|
||||
self.used = True
|
||||
return u''
|
||||
return self.sep
|
||||
|
||||
|
||||
# Imported here because that's where it was in the past
|
||||
from .markupsafe import Markup, escape, soft_unicode
|
||||
Vendored
-87
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jinja2.visitor
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module implements a visitor for the nodes.
|
||||
|
||||
:copyright: (c) 2010 by the Jinja Team.
|
||||
:license: BSD.
|
||||
"""
|
||||
from jinja2.nodes import Node
|
||||
|
||||
|
||||
class NodeVisitor(object):
|
||||
"""Walks the abstract syntax tree and call visitor functions for every
|
||||
node found. The visitor functions may return values which will be
|
||||
forwarded by the `visit` method.
|
||||
|
||||
Per default the visitor functions for the nodes are ``'visit_'`` +
|
||||
class name of the node. So a `TryFinally` node visit function would
|
||||
be `visit_TryFinally`. This behavior can be changed by overriding
|
||||
the `get_visitor` function. If no visitor function exists for a node
|
||||
(return value `None`) the `generic_visit` visitor is used instead.
|
||||
"""
|
||||
|
||||
def get_visitor(self, node):
|
||||
"""Return the visitor function for this node or `None` if no visitor
|
||||
exists for this node. In that case the generic visit function is
|
||||
used instead.
|
||||
"""
|
||||
method = 'visit_' + node.__class__.__name__
|
||||
return getattr(self, method, None)
|
||||
|
||||
def visit(self, node, *args, **kwargs):
|
||||
"""Visit a node."""
|
||||
f = self.get_visitor(node)
|
||||
if f is not None:
|
||||
return f(node, *args, **kwargs)
|
||||
return self.generic_visit(node, *args, **kwargs)
|
||||
|
||||
def generic_visit(self, node, *args, **kwargs):
|
||||
"""Called if no explicit visitor function exists for a node."""
|
||||
for node in node.iter_child_nodes():
|
||||
self.visit(node, *args, **kwargs)
|
||||
|
||||
|
||||
class NodeTransformer(NodeVisitor):
|
||||
"""Walks the abstract syntax tree and allows modifications of nodes.
|
||||
|
||||
The `NodeTransformer` will walk the AST and use the return value of the
|
||||
visitor functions to replace or remove the old node. If the return
|
||||
value of the visitor function is `None` the node will be removed
|
||||
from the previous location otherwise it's replaced with the return
|
||||
value. The return value may be the original node in which case no
|
||||
replacement takes place.
|
||||
"""
|
||||
|
||||
def generic_visit(self, node, *args, **kwargs):
|
||||
for field, old_value in node.iter_fields():
|
||||
if isinstance(old_value, list):
|
||||
new_values = []
|
||||
for value in old_value:
|
||||
if isinstance(value, Node):
|
||||
value = self.visit(value, *args, **kwargs)
|
||||
if value is None:
|
||||
continue
|
||||
elif not isinstance(value, Node):
|
||||
new_values.extend(value)
|
||||
continue
|
||||
new_values.append(value)
|
||||
old_value[:] = new_values
|
||||
elif isinstance(old_value, Node):
|
||||
new_node = self.visit(old_value, *args, **kwargs)
|
||||
if new_node is None:
|
||||
delattr(node, field)
|
||||
else:
|
||||
setattr(node, field, new_node)
|
||||
return node
|
||||
|
||||
def visit_list(self, node, *args, **kwargs):
|
||||
"""As transformers may return lists in some places this method
|
||||
can be used to enforce a list as return value.
|
||||
"""
|
||||
rv = self.visit(node, *args, **kwargs)
|
||||
if not isinstance(rv, list):
|
||||
rv = [rv]
|
||||
return rv
|
||||
Vendored
+7
-5
@@ -28,11 +28,13 @@ file(GLOB lib_srcs *.c)
|
||||
file(GLOB lib_hdrs *.h)
|
||||
|
||||
|
||||
if(ENABLE_NEON)
|
||||
list(APPEND lib_srcs arm/arm_init.c arm/filter_neon.S arm/filter_neon_intrinsics.c)
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=2)
|
||||
elseif(AARCH64)
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=0) # NEON assembler is not supported
|
||||
if(ARM OR AARCH64)
|
||||
if(ENABLE_NEON AND NOT AARCH64)
|
||||
list(APPEND lib_srcs arm/arm_init.c arm/filter_neon.S arm/filter_neon_intrinsics.c)
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=2)
|
||||
else()
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=0) # NEON assembler is not supported
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_SSE
|
||||
|
||||
Vendored
+4
-2
@@ -11,11 +11,13 @@ check_include_file(assert.h HAVE_ASSERT_H)
|
||||
check_include_file(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_file(io.h HAVE_IO_H)
|
||||
check_function_exists(jbg_newlen HAVE_JBG_NEWLEN)
|
||||
check_function_exists(mmap HAVE_MMAP)
|
||||
check_include_file(search.h HAVE_SEARCH_H)
|
||||
check_include_file(string.h HAVE_STRING_H)
|
||||
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
if(NOT MSVC)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
check_function_exists(mmap HAVE_MMAP)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT WINRT)
|
||||
set(USE_WIN32_FILEIO 1)
|
||||
|
||||
Vendored
+1
-1
@@ -3707,7 +3707,7 @@ TIFFReadDirectory(TIFF* tif)
|
||||
case TIFFTAG_SMAXSAMPLEVALUE:
|
||||
{
|
||||
|
||||
double *data;
|
||||
double *data = 0;
|
||||
enum TIFFReadDirEntryErr err;
|
||||
uint32 saved_flags;
|
||||
int m;
|
||||
|
||||
Vendored
+8
-9
@@ -5,10 +5,12 @@
|
||||
project(${WEBP_LIBRARY})
|
||||
|
||||
ocv_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/cpu-features")
|
||||
if(ANDROID)
|
||||
ocv_include_directories(${CPUFEATURES_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
file(GLOB lib_srcs dec/*.c dsp/*.c enc/*.c mux/*.c utils/*.c webp/*.c)
|
||||
file(GLOB lib_hdrs dec/*.h dsp/*.h enc/*.h mux/*.h utils/*.h webp/*.h)
|
||||
file(GLOB lib_srcs dec/*.c demux/*.c dsp/*.c enc/*.c mux/*.c utils/*.c webp/*.c)
|
||||
file(GLOB lib_hdrs dec/*.h demux/*.h dsp/*.h enc/*.h mux/*.h utils/*.h webp/*.h)
|
||||
|
||||
# FIXIT
|
||||
if(ANDROID AND ARMEABI_V7A AND NOT NEON)
|
||||
@@ -19,13 +21,7 @@ if(ANDROID AND ARMEABI_V7A AND NOT NEON)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
file(GLOB cpuf_s cpu-features/*.c)
|
||||
file(GLOB cpuf_h cpu-features/*.h)
|
||||
|
||||
if(ANDROID)
|
||||
set(lib_srcs ${lib_srcs} ${cpuf_s})
|
||||
set(lib_hdrs ${lib_hdrs} ${cpuf_h})
|
||||
endif()
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
# Define the library target:
|
||||
@@ -34,6 +30,9 @@ endif()
|
||||
add_definitions(-DWEBP_USE_THREAD)
|
||||
|
||||
add_library(${WEBP_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
||||
if(ANDROID)
|
||||
target_link_libraries(${WEBP_LIBRARY} ${CPUFEATURES_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC)
|
||||
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
noinst_LTLIBRARIES = libwebpdecode.la
|
||||
|
||||
libwebpdecode_la_SOURCES =
|
||||
libwebpdecode_la_SOURCES += alpha_dec.c
|
||||
libwebpdecode_la_SOURCES += alphai_dec.h
|
||||
libwebpdecode_la_SOURCES += buffer_dec.c
|
||||
libwebpdecode_la_SOURCES += common_dec.h
|
||||
libwebpdecode_la_SOURCES += vp8_dec.h
|
||||
libwebpdecode_la_SOURCES += frame_dec.c
|
||||
libwebpdecode_la_SOURCES += idec_dec.c
|
||||
libwebpdecode_la_SOURCES += io_dec.c
|
||||
libwebpdecode_la_SOURCES += quant_dec.c
|
||||
libwebpdecode_la_SOURCES += tree_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8i_dec.h
|
||||
libwebpdecode_la_SOURCES += vp8l_dec.c
|
||||
libwebpdecode_la_SOURCES += vp8li_dec.h
|
||||
libwebpdecode_la_SOURCES += webp_dec.c
|
||||
libwebpdecode_la_SOURCES += webpi_dec.h
|
||||
|
||||
libwebpdecodeinclude_HEADERS =
|
||||
libwebpdecodeinclude_HEADERS += ../webp/decode.h
|
||||
libwebpdecodeinclude_HEADERS += ../webp/types.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += ../webp/format_constants.h
|
||||
|
||||
libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE)
|
||||
libwebpdecodeincludedir = $(includedir)/webp
|
||||
Vendored
-115
@@ -1,115 +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 "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels_dec.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
||||
// The 'output' buffer should be pre-allocated and must be of the same
|
||||
// dimension 'height'x'width', as that of the image.
|
||||
//
|
||||
// Returns 1 on successfully decoding the compressed alpha and
|
||||
// 0 if either:
|
||||
// error in bit-stream header (invalid compression mode or filter), or
|
||||
// error returned by appropriate compression method.
|
||||
|
||||
static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
int width, int height, uint8_t* output) {
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
int rsrv;
|
||||
int ok = 0;
|
||||
int method;
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
|
||||
assert(width > 0 && height > 0);
|
||||
assert(data != NULL && output != NULL);
|
||||
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
method = (data[0] >> 0) & 0x03;
|
||||
filter = (data[0] >> 2) & 0x03;
|
||||
pre_processing = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (method < ALPHA_NO_COMPRESSION ||
|
||||
method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
filter >= WEBP_FILTER_LAST ||
|
||||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = height * width;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
if (ok) memcpy(output, alpha_data, alpha_decoded_size);
|
||||
} else {
|
||||
ok = VP8LDecodeAlphaImageStream(width, height, alpha_data, alpha_data_size,
|
||||
output);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
WebPUnfilterFunc unfilter_func = WebPUnfilters[filter];
|
||||
if (unfilter_func != NULL) {
|
||||
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
|
||||
// and apply filter per image-row.
|
||||
unfilter_func(width, height, width, output);
|
||||
}
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(output, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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) {
|
||||
// Decode everything during the first call.
|
||||
assert(!dec->is_alpha_decoded_);
|
||||
if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
|
||||
width, height, dec->alpha_plane_)) {
|
||||
return NULL; // Error.
|
||||
}
|
||||
dec->is_alpha_decoded_ = 1;
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * width;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
Vendored
+232
@@ -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;
|
||||
}
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha decoder: internal header.
|
||||
//
|
||||
// Author: Urvang (urvang@google.com)
|
||||
|
||||
#ifndef WEBP_DEC_ALPHAI_H_
|
||||
#define WEBP_DEC_ALPHAI_H_
|
||||
|
||||
#include "./webpi_dec.h"
|
||||
#include "../utils/filters_utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct VP8LDecoder; // Defined in dec/vp8li.h.
|
||||
|
||||
typedef struct ALPHDecoder ALPHDecoder;
|
||||
struct ALPHDecoder {
|
||||
int width_;
|
||||
int height_;
|
||||
int method_;
|
||||
WEBP_FILTER_TYPE filter_;
|
||||
int pre_processing_;
|
||||
struct VP8LDecoder* vp8l_dec_;
|
||||
VP8Io io_;
|
||||
int use_8b_decode_; // Although alpha channel requires only 1 byte per
|
||||
// pixel, sometimes VP8LDecoder may need to allocate
|
||||
// 4 bytes per pixel internally during decode.
|
||||
uint8_t* output_;
|
||||
const uint8_t* prev_line_; // last output row (or NULL)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// Deallocate memory associated to dec->alpha_plane_ decoding
|
||||
void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_DEC_ALPHAI_H_ */
|
||||
+111
-28
@@ -13,14 +13,10 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./webpi.h"
|
||||
#include "./vp8i_dec.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
@@ -37,6 +33,11 @@ static int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
// strictly speaking, the very last (or first, if flipped) row
|
||||
// doesn't require padding.
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
(uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH)
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||
@@ -46,33 +47,41 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const uint64_t y_size = (uint64_t)buf->y_stride * height;
|
||||
const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
|
||||
const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
|
||||
const uint64_t a_size = (uint64_t)buf->a_stride * height;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int y_stride = abs(buf->y_stride);
|
||||
const int u_stride = abs(buf->u_stride);
|
||||
const int v_stride = abs(buf->v_stride);
|
||||
const int a_stride = abs(buf->a_stride);
|
||||
const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
|
||||
const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
|
||||
const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
|
||||
const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
|
||||
ok &= (y_size <= buf->y_size);
|
||||
ok &= (u_size <= buf->u_size);
|
||||
ok &= (v_size <= buf->v_size);
|
||||
ok &= (buf->y_stride >= width);
|
||||
ok &= (buf->u_stride >= (width + 1) / 2);
|
||||
ok &= (buf->v_stride >= (width + 1) / 2);
|
||||
ok &= (y_stride >= width);
|
||||
ok &= (u_stride >= uv_width);
|
||||
ok &= (v_stride >= uv_width);
|
||||
ok &= (buf->y != NULL);
|
||||
ok &= (buf->u != NULL);
|
||||
ok &= (buf->v != NULL);
|
||||
if (mode == MODE_YUVA) {
|
||||
ok &= (buf->a_stride >= width);
|
||||
ok &= (a_stride >= width);
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const uint64_t size = (uint64_t)buf->stride * height;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (buf->stride >= width * kModeBpp[mode]);
|
||||
ok &= (stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
}
|
||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
#undef MIN_BUFFER_SIZE
|
||||
|
||||
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
const int w = buffer->width;
|
||||
@@ -83,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
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;
|
||||
int uv_stride = 0, a_stride = 0;
|
||||
uint64_t uv_size = 0, a_size = 0, total_size;
|
||||
@@ -135,9 +144,35 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
return CheckDecBuffer(buffer);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
|
||||
if (buffer == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (WebPIsRGBMode(buffer->colorspace)) {
|
||||
WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
buf->rgba += (buffer->height - 1) * buf->stride;
|
||||
buf->stride = -buf->stride;
|
||||
} else {
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int H = buffer->height;
|
||||
buf->y += (H - 1) * buf->y_stride;
|
||||
buf->y_stride = -buf->y_stride;
|
||||
buf->u += ((H - 1) >> 1) * buf->u_stride;
|
||||
buf->u_stride = -buf->u_stride;
|
||||
buf->v += ((H - 1) >> 1) * buf->v_stride;
|
||||
buf->v_stride = -buf->v_stride;
|
||||
if (buf->a != NULL) {
|
||||
buf->a += (H - 1) * buf->a_stride;
|
||||
buf->a_stride = -buf->a_stride;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const out) {
|
||||
VP8StatusCode status;
|
||||
if (out == NULL || w <= 0 || h <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
@@ -154,18 +189,28 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
||||
h = ch;
|
||||
}
|
||||
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;
|
||||
}
|
||||
w = options->scaled_width;
|
||||
h = options->scaled_height;
|
||||
w = scaled_width;
|
||||
h = scaled_height;
|
||||
}
|
||||
}
|
||||
out->width = w;
|
||||
out->height = h;
|
||||
|
||||
// Then, allocate buffer for real
|
||||
return AllocateBuffer(out);
|
||||
// Then, allocate buffer for real.
|
||||
status = AllocateBuffer(out);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
|
||||
// Use the stride trick if vertical flip is needed.
|
||||
if (options != NULL && options->flip) {
|
||||
status = WebPFlipBuffer(out);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -182,8 +227,9 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
|
||||
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
||||
if (buffer != NULL) {
|
||||
if (!buffer->is_external_memory)
|
||||
free(buffer->private_memory);
|
||||
if (buffer->is_external_memory <= 0) {
|
||||
WebPSafeFree(buffer->private_memory);
|
||||
}
|
||||
buffer->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@@ -210,8 +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);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vendored
+54
@@ -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_
|
||||
+369
-251
@@ -12,14 +12,183 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8i_dec.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
#define ALIGN_MASK (32 - 1)
|
||||
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,
|
||||
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
|
||||
@@ -31,25 +200,18 @@ extern "C" {
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||
|
||||
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
|
||||
if (keyframe) {
|
||||
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
return (level >= 40) ? 3 : (level >= 20) ? 2 : (level >= 15) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int y_bps = dec->cache_y_stride_;
|
||||
VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + ctx->id_ * 16 * y_bps + mb_x * 16;
|
||||
const int level = f_info->f_level_;
|
||||
const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
|
||||
const int ilevel = f_info->f_ilevel_;
|
||||
const int limit = 2 * level + ilevel;
|
||||
if (level == 0) {
|
||||
const int limit = f_info->f_limit_;
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
assert(limit >= 3);
|
||||
if (dec->filter_type_ == 1) { // simple
|
||||
if (mb_x > 0) {
|
||||
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
|
||||
@@ -65,10 +227,9 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
}
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh =
|
||||
hev_thresh_from_level(level, dec->frm_hdr_.key_frame_);
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh_;
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
@@ -121,32 +282,110 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
||||
int level = base_level;
|
||||
if (hdr->use_lf_delta_) {
|
||||
// TODO(skal): only CURRENT is handled for now.
|
||||
level += hdr->ref_lf_delta_[0];
|
||||
if (i4x4) {
|
||||
level += hdr->mode_lf_delta_[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
info->f_level_ = level;
|
||||
|
||||
if (hdr->sharpness_ > 0) {
|
||||
if (hdr->sharpness_ > 4) {
|
||||
level >>= 2;
|
||||
} else {
|
||||
level >>= 1;
|
||||
}
|
||||
if (level > 9 - hdr->sharpness_) {
|
||||
level = 9 - hdr->sharpness_;
|
||||
if (level > 0) {
|
||||
int ilevel = level;
|
||||
if (hdr->sharpness_ > 0) {
|
||||
if (hdr->sharpness_ > 4) {
|
||||
ilevel >>= 2;
|
||||
} else {
|
||||
ilevel >>= 1;
|
||||
}
|
||||
if (ilevel > 9 - hdr->sharpness_) {
|
||||
ilevel = 9 - hdr->sharpness_;
|
||||
}
|
||||
}
|
||||
if (ilevel < 1) ilevel = 1;
|
||||
info->f_ilevel_ = ilevel;
|
||||
info->f_limit_ = 2 * level + ilevel;
|
||||
info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
info->f_limit_ = 0; // no filtering
|
||||
}
|
||||
info->f_ilevel_ = (level < 1) ? 1 : level;
|
||||
info->f_inner_ = 0;
|
||||
info->f_inner_ = i4x4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dithering
|
||||
|
||||
// minimal amp that will provide a non-zero dithering effect
|
||||
#define MIN_DITHER_AMP 4
|
||||
|
||||
#define DITHER_AMP_TAB_SIZE 12
|
||||
static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
||||
// roughly, it's dqm->uv_mat_[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec) {
|
||||
assert(dec != NULL);
|
||||
if (options != NULL) {
|
||||
const int d = options->dithering_strength;
|
||||
const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
|
||||
const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
|
||||
if (f > 0) {
|
||||
int s;
|
||||
int all_amp = 0;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8QuantMatrix* const dqm = &dec->dqm_[s];
|
||||
if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
|
||||
// TODO(skal): should we specially dither more for uv_quant_ < 0?
|
||||
const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
|
||||
dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
|
||||
}
|
||||
all_amp |= dqm->dither_;
|
||||
}
|
||||
if (all_amp != 0) {
|
||||
VP8InitRandom(&dec->dithering_rg_, 1.0f);
|
||||
dec->dither_ = 1;
|
||||
}
|
||||
}
|
||||
// potentially allow alpha dithering
|
||||
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
||||
if (dec->alpha_dithering_ > 100) {
|
||||
dec->alpha_dithering_ = 100;
|
||||
} else if (dec->alpha_dithering_ < 0) {
|
||||
dec->alpha_dithering_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
|
||||
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
||||
uint8_t dither[64];
|
||||
int i;
|
||||
for (i = 0; i < 8 * 8; ++i) {
|
||||
dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp);
|
||||
}
|
||||
VP8DitherCombine8x8(dither, dst, bps);
|
||||
}
|
||||
|
||||
static void DitherRow(VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
assert(dec->dither_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const VP8MBData* const data = ctx->mb_data_ + mb_x;
|
||||
const int cache_id = ctx->id_;
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
if (data->dither_ >= MIN_DITHER_AMP) {
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
|
||||
Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// This function is called after a row of macroblocks is finished decoding.
|
||||
// It also takes into account the following restrictions:
|
||||
@@ -164,25 +403,35 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride_;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
|
||||
const int y_offset = ctx->id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = ctx->id_ * 8 * dec->cache_uv_stride_;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
|
||||
const int first_row = (ctx->mb_y_ == 0);
|
||||
const int last_row = (ctx->mb_y_ >= dec->br_mb_y_ - 1);
|
||||
int y_start = MACROBLOCK_VPOS(ctx->mb_y_);
|
||||
int y_end = MACROBLOCK_VPOS(ctx->mb_y_ + 1);
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int is_first_row = (mb_y == 0);
|
||||
const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
|
||||
|
||||
if (dec->mt_method_ == 2) {
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
|
||||
if (ctx->filter_row_) {
|
||||
FilterRow(dec);
|
||||
}
|
||||
|
||||
if (io->put) {
|
||||
if (!first_row) {
|
||||
if (dec->dither_) {
|
||||
DitherRow(dec);
|
||||
}
|
||||
|
||||
if (io->put != NULL) {
|
||||
int y_start = MACROBLOCK_VPOS(mb_y);
|
||||
int y_end = MACROBLOCK_VPOS(mb_y + 1);
|
||||
if (!is_first_row) {
|
||||
y_start -= extra_y_rows;
|
||||
io->y = ydst;
|
||||
io->u = udst;
|
||||
@@ -193,7 +442,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
io->v = dec->cache_v_ + uv_offset;
|
||||
}
|
||||
|
||||
if (!last_row) {
|
||||
if (!is_last_row) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
@@ -201,12 +450,9 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
// TODO(skal): several things to correct here:
|
||||
// * testing presence of alpha with dec->alpha_data_ is not a good idea
|
||||
// * we're actually decompressing the full plane only once. It should be
|
||||
// more obvious from signature.
|
||||
// * we could free alpha_data_ right after this call, but we don't own.
|
||||
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
|
||||
// TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
|
||||
// good idea.
|
||||
io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Could not decode alpha data.");
|
||||
@@ -237,8 +483,8 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
}
|
||||
// rotate top samples if needed
|
||||
if (ctx->id_ + 1 == dec->num_caches_) {
|
||||
if (!last_row) {
|
||||
if (cache_id + 1 == dec->num_caches_) {
|
||||
if (!is_last_row) {
|
||||
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
|
||||
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
@@ -255,27 +501,40 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
if (!dec->use_threads_) {
|
||||
const int filter_row =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
if (dec->mt_method_ == 0) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
ReconstructRow(dec, ctx);
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPWorkerSync(worker);
|
||||
ok &= WebPGetWorkerInterface()->Sync(worker);
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
if (ctx->filter_row_) { // just swap filter info
|
||||
ctx->filter_row_ = filter_row;
|
||||
if (dec->mt_method_ == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data_;
|
||||
ctx->mb_data_ = dec->mb_data_;
|
||||
dec->mb_data_ = tmp;
|
||||
} else {
|
||||
// perform reconstruction directly in main thread
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
}
|
||||
WebPWorkerLaunch(worker);
|
||||
// (reconstruct)+filter in parallel
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
@@ -289,8 +548,8 @@ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() not matter what.
|
||||
if (io->setup && !io->setup(io)) {
|
||||
// Note: Afterward, we must call teardown() no matter what.
|
||||
if (io->setup != NULL && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
}
|
||||
@@ -303,7 +562,7 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
//
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock and
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock
|
||||
// and filters one. It doesn't filter the chroma samples. Hence, we can
|
||||
// avoid doing the in-loop filtering before crop_top/crop_left position.
|
||||
// For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
|
||||
@@ -344,11 +603,11 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
if (dec->use_threads_) {
|
||||
ok = WebPWorkerSync(&dec->worker_);
|
||||
if (dec->mt_method_ > 0) {
|
||||
ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
|
||||
}
|
||||
|
||||
if (io->teardown) {
|
||||
if (io->teardown != NULL) {
|
||||
io->teardown(io);
|
||||
}
|
||||
return ok;
|
||||
@@ -384,9 +643,9 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id_ = 0;
|
||||
if (dec->use_threads_) {
|
||||
if (dec->mt_method_ > 0) {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
if (!WebPWorkerReset(worker)) {
|
||||
if (!WebPGetWorkerInterface()->Reset(worker)) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"thread initialization failed.");
|
||||
}
|
||||
@@ -401,6 +660,28 @@ static int InitThreadContext(VP8Decoder* const dec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height) {
|
||||
if (options == NULL || options->use_threads == 0) {
|
||||
return 0;
|
||||
}
|
||||
(void)headers;
|
||||
(void)width;
|
||||
(void)height;
|
||||
assert(headers == NULL || !headers->is_lossless);
|
||||
#if defined(WEBP_USE_THREAD)
|
||||
if (width < MIN_WIDTH_FOR_THREADS) return 0;
|
||||
// TODO(skal): tune the heuristic further
|
||||
#if 0
|
||||
if (height < 2 * width) return 2;
|
||||
#endif
|
||||
return 2;
|
||||
#else // !WEBP_USE_THREAD
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef MT_CACHE_LINES
|
||||
#undef ST_CACHE_LINES
|
||||
|
||||
@@ -412,14 +693,15 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
const int mb_w = dec->mb_w_;
|
||||
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
|
||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||
const size_t top_size = (16 + 8 + 8) * mb_w;
|
||||
const size_t top_size = sizeof(VP8TopSamples) * mb_w;
|
||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||
const size_t f_info_size =
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
|
||||
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
|
||||
const size_t mb_data_size =
|
||||
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
@@ -428,20 +710,20 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + coeffs_size
|
||||
+ cache_size + alpha_size + ALIGN_MASK;
|
||||
+ yuv_size + mb_data_size
|
||||
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
uint8_t* mem;
|
||||
|
||||
if (needed != (size_t)needed) return 0; // check for overflow
|
||||
if (needed > dec->mem_size_) {
|
||||
free(dec->mem_);
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_size_ = 0;
|
||||
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem_ == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"no memory during frame initialization.");
|
||||
}
|
||||
// down-cast is ok, thanks to WebPSafeAlloc() above.
|
||||
// down-cast is ok, thanks to WebPSafeMalloc() above.
|
||||
dec->mem_size_ = (size_t)needed;
|
||||
}
|
||||
|
||||
@@ -449,12 +731,8 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
dec->intra_t_ = (uint8_t*)mem;
|
||||
mem += intra_pred_mode_size;
|
||||
|
||||
dec->y_t_ = (uint8_t*)mem;
|
||||
mem += 16 * mb_w;
|
||||
dec->u_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
dec->v_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
dec->yuv_t_ = (VP8TopSamples*)mem;
|
||||
mem += top_size;
|
||||
|
||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||
mem += mb_info_size;
|
||||
@@ -463,20 +741,24 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
mem += f_info_size;
|
||||
dec->thread_ctx_.id_ = 0;
|
||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||
if (dec->use_threads_) {
|
||||
if (dec->mt_method_ > 0) {
|
||||
// secondary cache line. The deblocking process need to make use of the
|
||||
// filtering strength from previous macroblock row, while the new ones
|
||||
// are being decoded in parallel. We'll just swap the pointers.
|
||||
dec->thread_ctx_.f_info_ += mb_w;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
||||
assert((yuv_size & ALIGN_MASK) == 0);
|
||||
mem = (uint8_t*)WEBP_ALIGN(mem);
|
||||
assert((yuv_size & WEBP_ALIGN_CST) == 0);
|
||||
dec->yuv_b_ = (uint8_t*)mem;
|
||||
mem += yuv_size;
|
||||
|
||||
dec->coeffs_ = (int16_t*)mem;
|
||||
mem += coeffs_size;
|
||||
dec->mb_data_ = (VP8MBData*)mem;
|
||||
dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
|
||||
if (dec->mt_method_ == 2) {
|
||||
dec->thread_ctx_.mb_data_ += mb_w;
|
||||
}
|
||||
mem += mb_data_size;
|
||||
|
||||
dec->cache_y_stride_ = 16 * mb_w;
|
||||
dec->cache_uv_stride_ = 8 * mb_w;
|
||||
@@ -498,8 +780,9 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
mem += alpha_size;
|
||||
assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
|
||||
|
||||
// note: left-info is initialized once for all.
|
||||
// note: left/top-info is initialized once for all.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
|
||||
// initialize top
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
@@ -518,7 +801,7 @@ static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||
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 (!AllocateMemory(dec)) return 0;
|
||||
InitIo(dec, io);
|
||||
@@ -527,168 +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 WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
if (dec->mb_x_ == 0) {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
|
||||
} else {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
|
||||
*(uint32_t*)dst = *(uint32_t*)src;
|
||||
}
|
||||
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec) {
|
||||
int j;
|
||||
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;
|
||||
|
||||
// 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 (dec->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 (dec->mb_y_ > 0) {
|
||||
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
|
||||
}
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
uint8_t* const top_y = dec->y_t_ + dec->mb_x_ * 16;
|
||||
uint8_t* const top_u = dec->u_t_ + dec->mb_x_ * 8;
|
||||
uint8_t* const top_v = dec->v_t_ + dec->mb_x_ * 8;
|
||||
const int16_t* coeffs = dec->coeffs_;
|
||||
int n;
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
memcpy(y_dst - BPS, top_y, 16);
|
||||
memcpy(u_dst - BPS, top_u, 8);
|
||||
memcpy(v_dst - BPS, top_v, 8);
|
||||
} else if (dec->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 (dec->is_i4x4_) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
if (dec->mb_x_ >= dec->mb_w_ - 1) { // on rightmost border
|
||||
top_right[0] = top_y[15] * 0x01010101u;
|
||||
} else {
|
||||
memcpy(top_right, top_y + 16, 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 residues for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[dec->imodes_[n]](dst);
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(dec, dec->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (dec->non_zero_) {
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const int pred_func = CheckMode(dec, dec->uvmode_);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
|
||||
if (dec->non_zero_ & 0x0f0000) { // chroma-U
|
||||
const int16_t* const u_coeffs = dec->coeffs_ + 16 * 16;
|
||||
if (dec->non_zero_ac_ & 0x0f0000) {
|
||||
VP8TransformUV(u_coeffs, u_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(u_coeffs, u_dst);
|
||||
}
|
||||
}
|
||||
if (dec->non_zero_ & 0xf00000) { // chroma-V
|
||||
const int16_t* const v_coeffs = dec->coeffs_ + 20 * 16;
|
||||
if (dec->non_zero_ac_ & 0xf00000) {
|
||||
VP8TransformUV(v_coeffs, v_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(v_coeffs, v_dst);
|
||||
}
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (dec->mb_y_ < dec->mb_h_ - 1) {
|
||||
memcpy(top_y, y_dst + 15 * BPS, 16);
|
||||
memcpy(top_u, u_dst + 7 * BPS, 8);
|
||||
memcpy(top_v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Transfer reconstructed samples from yuv_b_ cache to final destination.
|
||||
{
|
||||
const int y_offset = dec->cache_id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = dec->cache_id_ * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const y_out = dec->cache_y_ + dec->mb_x_ * 16 + y_offset;
|
||||
uint8_t* const u_out = dec->cache_u_ + dec->mb_x_ * 8 + uv_offset;
|
||||
uint8_t* const v_out = dec->cache_v_ + dec->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
+175
-123
@@ -15,14 +15,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./webpi.h"
|
||||
#include "./vp8i.h"
|
||||
#include "./alphai_dec.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "./vp8i_dec.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// In append mode, buffer allocations increase as multiples of this value.
|
||||
// Needs to be a power of 2.
|
||||
#define CHUNK_SIZE 4096
|
||||
@@ -31,11 +28,13 @@ extern "C" {
|
||||
//------------------------------------------------------------------------------
|
||||
// Data structures for memory and states
|
||||
|
||||
// Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
|
||||
// Decoding states. State normally flows as:
|
||||
// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
|
||||
// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
|
||||
// If there is any error the decoder goes into state ERROR.
|
||||
typedef enum {
|
||||
STATE_PRE_VP8, // All data before that of the first VP8 chunk.
|
||||
STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
|
||||
STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
|
||||
STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
|
||||
STATE_VP8_PARTS0,
|
||||
STATE_VP8_DATA,
|
||||
STATE_VP8L_HEADER,
|
||||
@@ -71,30 +70,24 @@ struct WebPIDecoder {
|
||||
VP8Io io_;
|
||||
|
||||
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.
|
||||
|
||||
int last_mb_y_; // last row reached for intra-mode decoding
|
||||
};
|
||||
|
||||
// MB context to restore in case VP8DecodeMB() fails
|
||||
typedef struct {
|
||||
VP8MB left_;
|
||||
VP8MB info_;
|
||||
uint8_t intra_t_[4];
|
||||
uint8_t intra_l_[4];
|
||||
VP8BitReader br_;
|
||||
VP8BitReader token_br_;
|
||||
} MBContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MemBuffer: incoming data handling
|
||||
|
||||
static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
|
||||
if (br->buf_ != NULL) {
|
||||
br->buf_ += offset;
|
||||
br->buf_end_ += offset;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
}
|
||||
@@ -102,7 +95,7 @@ static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
|
||||
// Check if we need to preserve the compressed alpha data, as it may not have
|
||||
// been decoded yet.
|
||||
static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
|
||||
if (idec->state_ == STATE_PRE_VP8) {
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
// We haven't parsed the headers yet, so we don't know whether the image is
|
||||
// lossy or lossless. This also means that we haven't parsed the ALPH chunk.
|
||||
return 0;
|
||||
@@ -111,7 +104,7 @@ static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
|
||||
return 0; // ALPH chunk is not present for lossless images.
|
||||
} else {
|
||||
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
assert(dec != NULL); // Must be true as idec->state_ != STATE_PRE_VP8.
|
||||
assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER.
|
||||
return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
|
||||
}
|
||||
}
|
||||
@@ -127,21 +120,39 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
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) {
|
||||
int p;
|
||||
uint32_t p;
|
||||
for (p = 0; p <= last_part; ++p) {
|
||||
RemapBitReader(dec->parts_ + p, offset);
|
||||
VP8RemapBitReader(dec->parts_ + p, offset);
|
||||
}
|
||||
// Remap partition #0 data pointer to new offset, but only in MAP
|
||||
// mode (in APPEND mode, partition #0 is copied into a fixed memory).
|
||||
if (mem->mode_ == MEM_MODE_MAP) {
|
||||
RemapBitReader(&dec->br_, offset);
|
||||
VP8RemapBitReader(&dec->br_, offset);
|
||||
}
|
||||
}
|
||||
{
|
||||
const uint8_t* const last_start = dec->parts_[last_part].buf_;
|
||||
VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
|
||||
mem->buf_ + mem->end_ - last_start);
|
||||
}
|
||||
if (NeedCompressedAlpha(idec)) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
dec->alpha_data_ += offset;
|
||||
if (alph_dec != NULL) {
|
||||
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
|
||||
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
|
||||
assert(alph_vp8l_dec != NULL);
|
||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
|
||||
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
|
||||
dec->alpha_data_ + ALPHA_HEADER_LEN,
|
||||
dec->alpha_data_size_ - ALPHA_HEADER_LEN);
|
||||
} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
|
||||
// Nothing special to do in this case.
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(last_part >= 0);
|
||||
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
|
||||
if (NeedCompressedAlpha(idec)) dec->alpha_data_ += offset;
|
||||
} else { // Resize lossless bitreader
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
|
||||
@@ -175,7 +186,7 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
|
||||
if (new_buf == NULL) return 0;
|
||||
memcpy(new_buf, old_base, current_size);
|
||||
free(mem->buf_);
|
||||
WebPSafeFree(mem->buf_);
|
||||
mem->buf_ = new_buf;
|
||||
mem->buf_size_ = (size_t)extra_size;
|
||||
mem->start_ = new_mem_start;
|
||||
@@ -217,8 +228,8 @@ static void InitMemBuffer(MemBuffer* const mem) {
|
||||
static void ClearMemBuffer(MemBuffer* const mem) {
|
||||
assert(mem);
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
free(mem->buf_);
|
||||
free((void*)mem->part0_buf_);
|
||||
WebPSafeFree(mem->buf_);
|
||||
WebPSafeFree((void*)mem->part0_buf_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,35 +243,40 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// To be called last.
|
||||
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||
const WebPDecoderOptions* const options = idec->params_.options;
|
||||
WebPDecBuffer* const output = idec->params_.output;
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
if (options != NULL && options->flip) {
|
||||
const VP8StatusCode status = WebPFlipBuffer(output);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
}
|
||||
if (idec->final_output_ != NULL) {
|
||||
WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
*output = *idec->final_output_;
|
||||
idec->final_output_ = NULL;
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Macroblock-decoding contexts
|
||||
|
||||
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
|
||||
MBContext* const context) {
|
||||
const VP8BitReader* const br = &dec->br_;
|
||||
const VP8MB* const left = dec->mb_info_ - 1;
|
||||
const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
context->left_ = *left;
|
||||
context->info_ = *info;
|
||||
context->br_ = *br;
|
||||
context->left_ = dec->mb_info_[-1];
|
||||
context->info_ = dec->mb_info_[dec->mb_x_];
|
||||
context->token_br_ = *token_br;
|
||||
memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
|
||||
memcpy(context->intra_l_, dec->intra_l_, 4);
|
||||
}
|
||||
|
||||
static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
*left = context->left_;
|
||||
*info = context->info_;
|
||||
*br = context->br_;
|
||||
dec->mb_info_[-1] = context->left_;
|
||||
dec->mb_info_[dec->mb_x_] = context->info_;
|
||||
*token_br = context->token_br_;
|
||||
memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
|
||||
memcpy(dec->intra_l_, context->intra_l_, 4);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -268,7 +284,7 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
if (io->teardown) {
|
||||
if (io->teardown != NULL) {
|
||||
io->teardown(io);
|
||||
}
|
||||
}
|
||||
@@ -296,6 +312,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = curr_size;
|
||||
headers.have_all_data = 0;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
|
||||
@@ -311,15 +328,9 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = (idec->params_.options != NULL) &&
|
||||
(idec->params_.options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
|
||||
ChangeState(idec, STATE_VP8_HEADER, headers.offset);
|
||||
} else {
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) {
|
||||
@@ -334,13 +345,14 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
int width, height;
|
||||
uint32_t bits;
|
||||
|
||||
if (curr_size < VP8_FRAME_HEADER_SIZE) {
|
||||
// Not enough data bytes to extract VP8 Frame Header.
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
|
||||
@@ -354,30 +366,32 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
}
|
||||
|
||||
// Partition #0
|
||||
static int CopyParts0Data(WebPIDecoder* const idec) {
|
||||
static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
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_;
|
||||
assert(!idec->is_lossless_);
|
||||
assert(mem->part0_buf_ == NULL);
|
||||
assert(psize > 0);
|
||||
assert(psize <= mem->part0_size_); // Format limit: no need for runtime check
|
||||
// the following is a format limitation, no need for runtime check:
|
||||
assert(part_size <= mem->part0_size_);
|
||||
if (part_size == 0) { // can't have zero-size partition #0
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
// We copy and grab ownership of the partition #0 data.
|
||||
uint8_t* const part0_buf = (uint8_t*)malloc(psize);
|
||||
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
|
||||
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;
|
||||
br->buf_ = part0_buf;
|
||||
br->buf_end_ = part0_buf + psize;
|
||||
VP8BitReaderSetBuffer(br, part0_buf, part_size);
|
||||
} else {
|
||||
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
||||
}
|
||||
mem->start_ += psize;
|
||||
return 1;
|
||||
mem->start_ += part_size;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
@@ -407,9 +421,14 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
// This change must be done before calling VP8InitFrame()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
|
||||
io->width, io->height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
|
||||
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().
|
||||
@@ -433,49 +452,53 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
|
||||
assert(dec->ready_);
|
||||
|
||||
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
||||
VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
if (dec->mb_x_ == 0) {
|
||||
VP8InitScanline(dec);
|
||||
if (idec->last_mb_y_ != dec->mb_y_) {
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
// note: normally, error shouldn't occur since we already have the whole
|
||||
// partition0 available here in DecodeRemaining(). Reaching EOF while
|
||||
// reading intra modes really means a BITSTREAM_ERROR.
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
idec->last_mb_y_ = dec->mb_y_;
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||
MBContext context;
|
||||
SaveContext(dec, token_br, &context);
|
||||
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
RestoreContext(&context, dec, token_br);
|
||||
// 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);
|
||||
}
|
||||
RestoreContext(&context, dec, token_br);
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
// Reconstruct and emit samples.
|
||||
VP8ReconstructBlock(dec);
|
||||
|
||||
// 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_;
|
||||
assert(idec->mem_.start_ <= idec->mem_.end_);
|
||||
}
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
// Synchronize the thread and check for errors.
|
||||
if (!VP8ExitCritical(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->ready_ = 0;
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
return FinishDecoding(idec);
|
||||
}
|
||||
|
||||
static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
|
||||
static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,
|
||||
VP8StatusCode status) {
|
||||
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
@@ -492,9 +515,15 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
|
||||
|
||||
// Wait until there's enough data for decoding header.
|
||||
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 (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
|
||||
curr_size < idec->chunk_size_) {
|
||||
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
// Allocate/verify output buffer now.
|
||||
@@ -513,33 +542,29 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// At present Lossless decoder can't decode image incrementally. So wait till
|
||||
// all the image data is aggregated before image can be decoded.
|
||||
if (curr_size < idec->chunk_size_) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
// Switch to incremental decoding if we don't have all the bytes available.
|
||||
dec->incremental_ = (curr_size < idec->chunk_size_);
|
||||
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
|
||||
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
|
||||
: FinishDecoding(idec);
|
||||
}
|
||||
|
||||
// Main decoding loop
|
||||
static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
VP8StatusCode status = VP8_STATUS_SUSPENDED;
|
||||
|
||||
if (idec->state_ == STATE_PRE_VP8) {
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
status = DecodeWebPHeaders(idec);
|
||||
} else {
|
||||
if (idec->dec_ == NULL) {
|
||||
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||
}
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_FRAME_HEADER) {
|
||||
if (idec->state_ == STATE_VP8_HEADER) {
|
||||
status = DecodeVP8FrameHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_PARTS0) {
|
||||
@@ -558,40 +583,65 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public functions
|
||||
// Internal constructor
|
||||
|
||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
|
||||
static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
|
||||
const WebPBitstreamFeatures* const features) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idec->state_ = STATE_PRE_VP8;
|
||||
idec->state_ = STATE_WEBP_HEADER;
|
||||
idec->chunk_size_ = 0;
|
||||
|
||||
idec->last_mb_y_ = -1;
|
||||
|
||||
InitMemBuffer(&idec->mem_);
|
||||
WebPInitDecBuffer(&idec->output_);
|
||||
VP8InitIo(&idec->io_);
|
||||
|
||||
WebPResetDecParams(&idec->params_);
|
||||
idec->params_.output = output_buffer ? output_buffer : &idec->output_;
|
||||
if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
|
||||
idec->params_.output = &idec->output_;
|
||||
idec->final_output_ = output_buffer;
|
||||
if (output_buffer != NULL) {
|
||||
idec->params_.output->colorspace = output_buffer->colorspace;
|
||||
}
|
||||
} else {
|
||||
idec->params_.output = output_buffer;
|
||||
idec->final_output_ = NULL;
|
||||
}
|
||||
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
|
||||
|
||||
return idec;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||
return NewDecoder(output_buffer, NULL);
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config) {
|
||||
WebPIDecoder* idec;
|
||||
WebPBitstreamFeatures tmp_features;
|
||||
WebPBitstreamFeatures* const features =
|
||||
(config == NULL) ? &tmp_features : &config->input;
|
||||
memset(&tmp_features, 0, sizeof(tmp_features));
|
||||
|
||||
// Parse the bitstream's features, if requested:
|
||||
if (data != NULL && data_size > 0 && config != NULL) {
|
||||
if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
|
||||
if (data != NULL && data_size > 0) {
|
||||
if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of the incremental decoder
|
||||
idec = WebPINewDecoder(config ? &config->output : NULL);
|
||||
idec = (config != NULL) ? NewDecoder(&config->output, features)
|
||||
: NewDecoder(NULL, features);
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -606,14 +656,18 @@ void WebPIDelete(WebPIDecoder* idec) {
|
||||
if (idec == NULL) return;
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Delete(idec->dec_);
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
VP8Delete((VP8Decoder*)idec->dec_);
|
||||
} else {
|
||||
VP8LDelete(idec->dec_);
|
||||
VP8LDelete((VP8LDecoder*)idec->dec_);
|
||||
}
|
||||
}
|
||||
ClearMemBuffer(&idec->mem_);
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
free(idec);
|
||||
WebPSafeFree(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -621,11 +675,11 @@ void WebPIDelete(WebPIDecoder* idec) {
|
||||
|
||||
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
||||
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;
|
||||
|
||||
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_stride = 0;
|
||||
} else { // A buffer was passed. Validate the other params.
|
||||
@@ -647,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* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride) {
|
||||
const int is_external_memory = (luma != NULL);
|
||||
const int is_external_memory = (luma != NULL) ? 1 : 0;
|
||||
WebPIDecoder* idec;
|
||||
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_stride = u_stride = v_stride = a_stride = 0;
|
||||
u = v = a = NULL;
|
||||
@@ -759,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
|
||||
if (idec->state_ <= STATE_VP8_PARTS0) {
|
||||
return NULL;
|
||||
}
|
||||
if (idec->final_output_ != NULL) {
|
||||
return NULL; // not yet slow-copied
|
||||
}
|
||||
return idec->params_.output;
|
||||
}
|
||||
|
||||
@@ -768,8 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (left != NULL) *left = 0;
|
||||
if (top != NULL) *top = 0;
|
||||
// TODO(skal): later include handling of rotations.
|
||||
if (src) {
|
||||
if (src != NULL) {
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = idec->params_.last_y;
|
||||
} else {
|
||||
@@ -823,7 +879,7 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data) {
|
||||
if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
|
||||
if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -834,7 +890,3 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
+183
-173
@@ -13,14 +13,11 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../dec/vp8i.h"
|
||||
#include "./webpi.h"
|
||||
#include "../dec/vp8i_dec.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../dsp/yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main YUV<->RGB conversion functions
|
||||
@@ -48,56 +45,16 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
// Point-sampling U/V sampler.
|
||||
static int EmitSampledRGB(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 WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h - 1;
|
||||
int j;
|
||||
for (j = 0; j < last; j += 2) {
|
||||
sample(y_src, y_src + io->y_stride, u_src, v_src,
|
||||
dst, dst + buf->stride, mb_w);
|
||||
y_src += 2 * io->y_stride;
|
||||
u_src += io->uv_stride;
|
||||
v_src += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
}
|
||||
if (j == last) { // Just do the last line twice
|
||||
sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
|
||||
}
|
||||
WebPDecBuffer* const output = p->output;
|
||||
WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const dst = buf->rgba + io->mb_y * buf->stride;
|
||||
WebPSamplerProcessPlane(io->y, io->y_stride,
|
||||
io->u, io->v, io->uv_stride,
|
||||
dst, buf->stride, io->mb_w, io->mb_h,
|
||||
WebPSamplers[output->colorspace]);
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
|
||||
@@ -119,7 +76,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
if (y == 0) {
|
||||
// First line is special cased. We mirror the u/v samples at boundary.
|
||||
upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
@@ -162,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 WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
||||
int j;
|
||||
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == mb_h);
|
||||
if (alpha != NULL) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||
@@ -178,10 +145,7 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
}
|
||||
} else if (buf->a != NULL) {
|
||||
// the user requested alpha, but there is none, set it to opaque.
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memset(dst, 0xff, mb_w * sizeof(*dst));
|
||||
dst += buf->a_stride;
|
||||
}
|
||||
FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -212,7 +176,8 @@ static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
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;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
@@ -223,21 +188,13 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
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)) {
|
||||
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
|
||||
num_rows, dst, buf->stride);
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
// has_alpha is true if there's non-trivial alpha to premultiply with.
|
||||
if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
@@ -245,7 +202,8 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
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;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
@@ -254,10 +212,13 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
@@ -268,6 +229,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
alpha += io->width;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||
}
|
||||
@@ -293,15 +256,39 @@ static int Rescale(const uint8_t* src, int src_stride,
|
||||
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
|
||||
Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
|
||||
Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
|
||||
WebPRescaler* const scaler = p->scaler_y;
|
||||
int num_lines_out = 0;
|
||||
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
||||
// Before rescaling, we premultiply the luma directly into the io->y
|
||||
// internal buffer. This is OK since these samples are not used for
|
||||
// intra-prediction (the top samples are saved in cache_y_/u_/v_).
|
||||
// But we need to cast the const away, though.
|
||||
WebPMultRows((uint8_t*)io->y, io->y_stride,
|
||||
io->a, io->width, io->mb_w, mb_h, 0);
|
||||
}
|
||||
num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
|
||||
Rescale(io->u, io->uv_stride, uv_mb_h, p->scaler_u);
|
||||
Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v);
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride;
|
||||
if (io->a != NULL) {
|
||||
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
|
||||
WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride,
|
||||
p->scaler_a->dst_width, num_lines_out, 1);
|
||||
}
|
||||
} else if (buf->a != NULL) {
|
||||
// the user requested alpha, but there is none, set it to opaque.
|
||||
assert(p->last_y + expected_num_lines_out <= io->scaled_height);
|
||||
FillAlphaPlane(dst_a, io->scaled_width, expected_num_lines_out,
|
||||
buf->a_stride);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -317,40 +304,46 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
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 uv_work_size = 2 * uv_out_width; // and for each u/v ones
|
||||
size_t tmp_size;
|
||||
int32_t* work;
|
||||
size_t tmp_size, rescaler_size;
|
||||
rescaler_t* work;
|
||||
WebPRescaler* scalers;
|
||||
const int num_rescalers = has_alpha ? 4 : 3;
|
||||
|
||||
tmp_size = work_size + 2 * uv_work_size;
|
||||
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
|
||||
if (has_alpha) {
|
||||
tmp_size += work_size;
|
||||
tmp_size += work_size * sizeof(*work);
|
||||
}
|
||||
p->memory = calloc(1, tmp_size * sizeof(*work));
|
||||
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (int32_t*)p->memory;
|
||||
WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
|
||||
work = (rescaler_t*)p->memory;
|
||||
|
||||
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,
|
||||
io->mb_w, out_width, io->mb_h, out_height,
|
||||
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,
|
||||
uv_in_width, uv_out_width,
|
||||
uv_in_height, uv_out_height,
|
||||
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,
|
||||
uv_in_width, uv_out_width,
|
||||
uv_in_height, uv_out_height,
|
||||
work + work_size + uv_work_size);
|
||||
p->emit = EmitRescaledYUV;
|
||||
|
||||
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,
|
||||
io->mb_w, out_width, io->mb_h, out_height,
|
||||
work + work_size + 2 * uv_work_size);
|
||||
p->emit_alpha = EmitRescaledAlphaYUV;
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -362,19 +355,19 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
const WebPYUV444Converter convert =
|
||||
WebPYUV444Converters[p->output->colorspace];
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* dst = buf->rgba + y_pos * buf->stride;
|
||||
int num_lines_out = 0;
|
||||
// For RGB rescaling, because of the YUV420, current scan position
|
||||
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
|
||||
WebPRescalerHasPendingOutput(&p->scaler_u)) {
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
|
||||
WebPRescalerExportRow(&p->scaler_y);
|
||||
WebPRescalerExportRow(&p->scaler_u);
|
||||
WebPRescalerExportRow(&p->scaler_v);
|
||||
convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
|
||||
dst, p->scaler_y.dst_width);
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_y) &&
|
||||
WebPRescalerHasPendingOutput(p->scaler_u)) {
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
assert(p->scaler_u->y_accum == p->scaler_v->y_accum);
|
||||
WebPRescalerExportRow(p->scaler_y);
|
||||
WebPRescalerExportRow(p->scaler_u);
|
||||
WebPRescalerExportRow(p->scaler_v);
|
||||
convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst,
|
||||
dst, p->scaler_y->dst_width);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
@@ -388,71 +381,75 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_lines_out = 0;
|
||||
while (j < mb_h) {
|
||||
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);
|
||||
const int u_lines_in =
|
||||
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
|
||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||
const int v_lines_in =
|
||||
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
|
||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
j += y_lines_in;
|
||||
uv_j += u_lines_in;
|
||||
num_lines_out += ExportRGB(p, num_lines_out);
|
||||
if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) {
|
||||
const int u_lines_in =
|
||||
WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j,
|
||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||
const int v_lines_in =
|
||||
WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j,
|
||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
uv_j += u_lines_in;
|
||||
}
|
||||
num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (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 int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
uint32_t non_opaque = 0;
|
||||
const int width = p->scaler_a->dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(p->scaler_a);
|
||||
non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && alpha_mask != 0xff) {
|
||||
if (is_premult_alpha && non_opaque) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
|
||||
int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
int num_lines_out = 0;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
const int width = p->scaler_a->dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
while (WebPRescalerHasPendingOutput(p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
|
||||
const uint32_t alpha_value = p->scaler_a->dst[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
@@ -465,15 +462,17 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
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) {
|
||||
WebPRescaler* const scaler = &p->scaler_a;
|
||||
int j = 0;
|
||||
int pos = 0;
|
||||
while (j < io->mb_h) {
|
||||
j += WebPRescalerImport(scaler, io->mb_h - j,
|
||||
io->a + j * io->width, io->width);
|
||||
pos += p->emit_alpha_row(p, pos);
|
||||
WebPRescaler* const scaler = p->scaler_a;
|
||||
int lines_left = expected_num_out_lines;
|
||||
const int y_end = p->last_y + lines_left;
|
||||
while (lines_left > 0) {
|
||||
const int row_offset = scaler->src_y - io->mb_y;
|
||||
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
|
||||
io->a + row_offset * io->width, io->width);
|
||||
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -486,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_height = (io->mb_h + 1) >> 1;
|
||||
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
|
||||
size_t tmp_size1, tmp_size2;
|
||||
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_size2 = 3 * out_width;
|
||||
@@ -496,30 +497,37 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
tmp_size1 += work_size;
|
||||
tmp_size2 += out_width;
|
||||
}
|
||||
p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
|
||||
rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
|
||||
|
||||
p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (int32_t*)p->memory;
|
||||
work = (rescaler_t*)p->memory;
|
||||
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,
|
||||
io->mb_w, out_width, io->mb_h, out_height,
|
||||
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,
|
||||
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
|
||||
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,
|
||||
io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
|
||||
work + 2 * work_size);
|
||||
p->emit = EmitRescaledRGB;
|
||||
WebPInitYUV444Converters();
|
||||
|
||||
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,
|
||||
io->mb_w, out_width, io->mb_h, out_height,
|
||||
work + 3 * work_size);
|
||||
p->emit_alpha = EmitRescaledAlphaRGB;
|
||||
if (p->output->colorspace == MODE_RGBA_4444 ||
|
||||
@@ -528,6 +536,7 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
} else {
|
||||
p->emit_alpha_row = ExportAlpha;
|
||||
}
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -548,7 +557,9 @@ static int CustomSetup(VP8Io* io) {
|
||||
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPInitUpsamplers();
|
||||
}
|
||||
if (io->use_scaling) {
|
||||
const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
|
||||
if (!ok) {
|
||||
@@ -556,11 +567,12 @@ static int CustomSetup(VP8Io* io) {
|
||||
}
|
||||
} else {
|
||||
if (is_rgb) {
|
||||
WebPInitSamplers();
|
||||
p->emit = EmitSampledRGB; // default
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
if (io->fancy_upsampling) {
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
const int uv_width = (io->mb_w + 1) >> 1;
|
||||
p->memory = malloc(io->mb_w + 2 * uv_width);
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width));
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error.
|
||||
}
|
||||
@@ -569,18 +581,20 @@ static int CustomSetup(VP8Io* io) {
|
||||
p->tmp_v = p->tmp_u + uv_width;
|
||||
p->emit = EmitFancyRGB;
|
||||
WebPInitUpsamplers();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
p->emit = EmitYUV;
|
||||
}
|
||||
if (is_alpha) { // need transparency output
|
||||
if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
|
||||
p->emit_alpha =
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
if (is_rgb) {
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,8 +617,8 @@ static int CustomPut(const VP8Io* io) {
|
||||
return 0;
|
||||
}
|
||||
num_lines_out = p->emit(io, p);
|
||||
if (p->emit_alpha) {
|
||||
p->emit_alpha(io, p);
|
||||
if (p->emit_alpha != NULL) {
|
||||
p->emit_alpha(io, p, num_lines_out);
|
||||
}
|
||||
p->last_y += num_lines_out;
|
||||
return 1;
|
||||
@@ -614,7 +628,7 @@ static int CustomPut(const VP8Io* io) {
|
||||
|
||||
static void CustomTeardown(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
free(p->memory);
|
||||
WebPSafeFree(p->memory);
|
||||
p->memory = NULL;
|
||||
}
|
||||
|
||||
@@ -629,7 +643,3 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -11,11 +11,7 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "./vp8i_dec.h"
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) {
|
||||
return v < 0 ? 0 : v > M ? M : v;
|
||||
@@ -104,12 +100,11 @@ void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
|
||||
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
|
||||
m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
+57
-120
@@ -11,12 +11,12 @@
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "vp8i.h"
|
||||
#include "./vp8i_dec.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
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef USE_GENERIC_TREE
|
||||
@@ -33,61 +33,12 @@ static const int8_t kYModesIntra4[18] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
|
||||
// inter prediction modes
|
||||
enum {
|
||||
LEFT4 = 0, ABOVE4 = 1, ZERO4 = 2, NEW4 = 3,
|
||||
NEARESTMV, NEARMV, ZEROMV, NEWMV, SPLITMV };
|
||||
|
||||
static const int8_t kYModesInter[8] = {
|
||||
-DC_PRED, 1,
|
||||
2, 3,
|
||||
-V_PRED, -H_PRED,
|
||||
-TM_PRED, -B_PRED
|
||||
};
|
||||
|
||||
static const int8_t kMBSplit[6] = {
|
||||
-3, 1,
|
||||
-2, 2,
|
||||
-0, -1
|
||||
};
|
||||
|
||||
static const int8_t kMVRef[8] = {
|
||||
-ZEROMV, 1,
|
||||
-NEARESTMV, 2,
|
||||
-NEARMV, 3,
|
||||
-NEWMV, -SPLITMV
|
||||
};
|
||||
|
||||
static const int8_t kMVRef4[6] = {
|
||||
-LEFT4, 1,
|
||||
-ABOVE4, 2,
|
||||
-ZERO4, -NEW4
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Inter
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t kYModeProbaInter0[4] = { 112, 86, 140, 37 };
|
||||
static const uint8_t kUVModeProbaInter0[3] = { 162, 101, 204 };
|
||||
static const uint8_t kMVProba0[2][NUM_MV_PROBAS] = {
|
||||
{ 162, 128, 225, 146, 172, 147, 214, 39,
|
||||
156, 128, 129, 132, 75, 145, 178, 206,
|
||||
239, 254, 254 },
|
||||
{ 164, 128, 204, 170, 119, 235, 140, 230,
|
||||
228, 128, 130, 130, 74, 148, 180, 203,
|
||||
236, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// Paragraph 13.5
|
||||
static const uint8_t
|
||||
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
// genereated using vp8_default_coef_probs() in entropy.c:129
|
||||
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
@@ -328,28 +279,38 @@ static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
|
||||
|
||||
void VP8ResetProba(VP8Proba* const proba) {
|
||||
memset(proba->segments_, 255u, sizeof(proba->segments_));
|
||||
memcpy(proba->coeffs_, CoeffsProba0, sizeof(CoeffsProba0));
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
memcpy(proba->mv_, kMVProba0, sizeof(kMVProba0));
|
||||
memcpy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
|
||||
memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
|
||||
#endif
|
||||
// proba->bands_[][] is initialized later
|
||||
}
|
||||
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * dec->mb_x_;
|
||||
static void ParseIntraMode(VP8BitReader* const br,
|
||||
VP8Decoder* const dec, int mb_x) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * mb_x;
|
||||
uint8_t* const left = dec->intra_l_;
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
dec->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
if (!dec->is_i4x4_) {
|
||||
VP8MBData* const block = dec->mb_data_ + mb_x;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0])
|
||||
? VP8GetBit(br, dec->proba_.segments_[1])
|
||||
: 2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
} else {
|
||||
block->segment_ = 0; // default for intra
|
||||
}
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_);
|
||||
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
if (!block->is_i4x4_) {
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
|
||||
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
|
||||
dec->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(top[0]));
|
||||
memset(left, ymode, 4 * sizeof(left[0]));
|
||||
block->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(*top));
|
||||
memset(left, ymode, 4 * sizeof(*left));
|
||||
} else {
|
||||
uint8_t* modes = dec->imodes_;
|
||||
uint8_t* modes = block->imodes_;
|
||||
int y;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int ymode = left[y];
|
||||
@@ -358,10 +319,10 @@ void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
const uint8_t* const prob = kBModesProba[top[x]][ymode];
|
||||
#ifdef USE_GENERIC_TREE
|
||||
// Generic tree-parsing
|
||||
int i = 0;
|
||||
do {
|
||||
int i = kYModesIntra4[VP8GetBit(br, prob[0])];
|
||||
while (i > 0) {
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
|
||||
} while (i > 0);
|
||||
}
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
@@ -376,15 +337,24 @@ void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
*modes++ = ymode;
|
||||
}
|
||||
memcpy(modes, top, 4 * sizeof(*top));
|
||||
modes += 4;
|
||||
left[y] = ymode;
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
dec->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
}
|
||||
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
ParseIntraMode(br, dec, mb_x);
|
||||
}
|
||||
return !dec->br_.eof_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -526,18 +496,13 @@ static const uint8_t
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t MVUpdateProba[2][NUM_MV_PROBAS] = {
|
||||
{ 237, 246, 253, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 250, 250,
|
||||
252, 254, 254 },
|
||||
{ 231, 243, 245, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 251, 251,
|
||||
254, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// 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) {
|
||||
VP8Proba* const proba = &dec->proba_;
|
||||
int t, b, c, p;
|
||||
@@ -545,47 +510,19 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
if (VP8GetBit(br, CoeffsUpdateProba[t][b][c][p])) {
|
||||
proba->coeffs_[t][b][c][p] = VP8GetValue(br, 8);
|
||||
}
|
||||
const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ?
|
||||
VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p];
|
||||
proba->bands_[t][b].probas_[c][p] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (b = 0; b < 16 + 1; ++b) {
|
||||
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br);
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8);
|
||||
}
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->frm_hdr_.key_frame_) {
|
||||
int i;
|
||||
dec->intra_p_ = VP8GetValue(br, 8);
|
||||
dec->last_p_ = VP8GetValue(br, 8);
|
||||
dec->golden_p_ = VP8GetValue(br, 8);
|
||||
if (VP8Get(br)) { // update y-mode
|
||||
for (i = 0; i < 4; ++i) {
|
||||
proba->ymode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
if (VP8Get(br)) { // update uv-mode
|
||||
for (i = 0; i < 3; ++i) {
|
||||
proba->uvmode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
// update MV
|
||||
for (i = 0; i < 2; ++i) {
|
||||
int k;
|
||||
for (k = 0; k < NUM_MV_PROBAS; ++k) {
|
||||
if (VP8GetBit(br, MVUpdateProba[i][k])) {
|
||||
const int v = VP8GetValue(br, 7);
|
||||
proba->mv_[i][k] = v ? v << 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
+205
-268
@@ -13,14 +13,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "./alphai_dec.h"
|
||||
#include "./vp8i_dec.h"
|
||||
#include "./vp8li_dec.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "../utils/bit_reader_inl_utils.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -28,6 +26,16 @@ int WebPGetDecoderVersion(void) {
|
||||
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Signature and pointer-to-function for GetCoeffs() variants below.
|
||||
|
||||
typedef int (*GetCoeffsFunc)(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out);
|
||||
static volatile GetCoeffsFunc GetCoeffs = NULL;
|
||||
|
||||
static void InitGetCoeffs(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder
|
||||
|
||||
@@ -47,12 +55,13 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
}
|
||||
|
||||
VP8Decoder* VP8New(void) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
|
||||
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
if (dec != NULL) {
|
||||
SetOk(dec);
|
||||
WebPWorkerInit(&dec->worker_);
|
||||
WebPGetWorkerInterface()->Init(&dec->worker_);
|
||||
dec->ready_ = 0;
|
||||
dec->num_parts_ = 1;
|
||||
dec->num_parts_minus_one_ = 0;
|
||||
InitGetCoeffs();
|
||||
}
|
||||
return dec;
|
||||
}
|
||||
@@ -71,16 +80,13 @@ const char* VP8StatusMessage(VP8Decoder* const dec) {
|
||||
void VP8Delete(VP8Decoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8Clear(dec);
|
||||
free(dec);
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
// TODO This check would be unnecessary if alpha decompression was separated
|
||||
// from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
|
||||
// something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
|
||||
// failure.
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
dec->error_msg_ = msg;
|
||||
@@ -123,6 +129,9 @@ int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
if (((bits >> 5)) >= chunk_size) { // partition_length
|
||||
return 0; // inconsistent size information.
|
||||
}
|
||||
if (w == 0 || h == 0) {
|
||||
return 0; // We don't support both width and height to be zero.
|
||||
}
|
||||
|
||||
if (width) {
|
||||
*width = w;
|
||||
@@ -192,25 +201,27 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
const uint8_t* sz = buf;
|
||||
const uint8_t* buf_end = buf + size;
|
||||
const uint8_t* part_start;
|
||||
int last_part;
|
||||
int p;
|
||||
size_t size_left = size;
|
||||
size_t last_part;
|
||||
size_t p;
|
||||
|
||||
dec->num_parts_ = 1 << VP8GetValue(br, 2);
|
||||
last_part = dec->num_parts_ - 1;
|
||||
part_start = buf + last_part * 3;
|
||||
if (buf_end < part_start) {
|
||||
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1;
|
||||
last_part = dec->num_parts_minus_one_;
|
||||
if (size < 3 * last_part) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
part_start = buf + last_part * 3;
|
||||
size_left -= last_part * 3;
|
||||
for (p = 0; p < last_part; ++p) {
|
||||
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
const uint8_t* part_end = part_start + psize;
|
||||
if (part_end > buf_end) part_end = buf_end;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, part_end);
|
||||
part_start = part_end;
|
||||
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
if (psize > size_left) psize = size_left;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
||||
part_start += psize;
|
||||
size_left -= psize;
|
||||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||
}
|
||||
@@ -249,7 +260,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
VP8PictureHeader* pic_hdr;
|
||||
VP8BitReader* br;
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
@@ -259,33 +269,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"null VP8Io passed to VP8GetHeaders()");
|
||||
}
|
||||
|
||||
// Process Pre-VP8 chunks.
|
||||
headers.data = io->data;
|
||||
headers.data_size = io->data_size;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return VP8SetError(dec, status, "Incorrect/incomplete header.");
|
||||
}
|
||||
if (headers.is_lossless) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Unexpected lossless format encountered.");
|
||||
}
|
||||
|
||||
if (dec->alpha_data_ == NULL) {
|
||||
assert(dec->alpha_data_size_ == 0);
|
||||
// We have NOT set alpha data yet. Set it now.
|
||||
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
|
||||
// WebPParseHeaders() is called more than once, as in incremental decoding
|
||||
// case.)
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
}
|
||||
|
||||
// Process the VP8 frame header.
|
||||
buf = headers.data + headers.offset;
|
||||
buf_size = headers.data_size - headers.offset;
|
||||
assert(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
|
||||
buf = io->data;
|
||||
buf_size = io->data_size;
|
||||
if (buf_size < 4) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Truncated header.");
|
||||
@@ -299,12 +284,14 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
frm_hdr->profile_ = (bits >> 1) & 7;
|
||||
frm_hdr->show_ = (bits >> 4) & 1;
|
||||
frm_hdr->partition_length_ = (bits >> 5);
|
||||
if (frm_hdr->profile_ > 3)
|
||||
if (frm_hdr->profile_ > 3) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Incorrect keyframe parameters.");
|
||||
if (!frm_hdr->show_)
|
||||
}
|
||||
if (!frm_hdr->show_) {
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Frame not displayable.");
|
||||
}
|
||||
buf += 3;
|
||||
buf_size -= 3;
|
||||
}
|
||||
@@ -329,21 +316,27 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
||||
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
||||
|
||||
// Setup default output area (can be later modified during io->setup())
|
||||
io->width = pic_hdr->width_;
|
||||
io->height = pic_hdr->height_;
|
||||
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->crop_top = 0;
|
||||
io->crop_left = 0;
|
||||
io->crop_right = io->width;
|
||||
io->crop_bottom = io->height;
|
||||
io->use_scaling = 0;
|
||||
io->scaled_width = io->width;
|
||||
io->scaled_height = io->height;
|
||||
|
||||
io->mb_w = io->width; // sanity check
|
||||
io->mb_h = io->height; // ditto
|
||||
|
||||
VP8ResetProba(&dec->proba_);
|
||||
ResetSegmentHeader(&dec->segment_hdr_);
|
||||
dec->segment_ = 0; // default for intra
|
||||
}
|
||||
|
||||
// Check if we have all the partition #0 available, and initialize dec->br_
|
||||
@@ -354,7 +347,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
|
||||
br = &dec->br_;
|
||||
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
|
||||
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
||||
buf += frm_hdr->partition_length_;
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
|
||||
@@ -381,63 +374,14 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
// Frame buffer marking
|
||||
if (!frm_hdr->key_frame_) {
|
||||
// Paragraph 9.7
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->buffer_flags_ = VP8Get(br) << 0; // update golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
|
||||
if (!(dec->buffer_flags_ & 1)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
|
||||
}
|
||||
if (!(dec->buffer_flags_ & 2)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
|
||||
}
|
||||
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
|
||||
#else
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Not a key frame.");
|
||||
#endif
|
||||
} else {
|
||||
dec->buffer_flags_ = 0x003 | 0x100;
|
||||
}
|
||||
|
||||
// Paragraph 9.8
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->update_proba_ = VP8Get(br);
|
||||
if (!dec->update_proba_) { // save for later restore
|
||||
dec->proba_saved_ = dec->proba_;
|
||||
}
|
||||
dec->buffer_flags_ &= 1 << 8;
|
||||
dec->buffer_flags_ |=
|
||||
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
|
||||
#else
|
||||
VP8Get(br); // just ignore the value of update_proba_
|
||||
#endif
|
||||
VP8Get(br); // ignore the value of update_proba_
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
// Extensions
|
||||
if (dec->pic_hdr_.colorspace_) {
|
||||
const size_t kTrailerSize = 8;
|
||||
const uint8_t kTrailerMarker = 0x01;
|
||||
const uint8_t* ext_buf = buf - kTrailerSize;
|
||||
size_t size;
|
||||
|
||||
if (frm_hdr->partition_length_ < kTrailerSize ||
|
||||
ext_buf[kTrailerSize - 1] != kTrailerMarker) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"RIFF: Inconsistent extra information.");
|
||||
}
|
||||
|
||||
// Layer
|
||||
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
|
||||
dec->layer_data_size_ = size;
|
||||
dec->layer_data_ = NULL; // will be set later
|
||||
dec->layer_colorspace_ = ext_buf[3];
|
||||
}
|
||||
#endif
|
||||
|
||||
// sanitized state
|
||||
dec->ready_ = 1;
|
||||
return 1;
|
||||
@@ -446,11 +390,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
//------------------------------------------------------------------------------
|
||||
// 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 kCat4[] = { 176, 155, 140, 135, 0 };
|
||||
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
||||
@@ -461,9 +400,6 @@ static const uint8_t kZigzag[16] = {
|
||||
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
||||
};
|
||||
|
||||
typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
|
||||
typedef const uint8_t (*ProbaCtxArray)[NUM_PROBAS];
|
||||
|
||||
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
|
||||
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
int v;
|
||||
@@ -497,19 +433,20 @@ static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
}
|
||||
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
// (and 0 if there's no coeff at all)
|
||||
static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
|
||||
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][ctx];
|
||||
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
|
||||
return 0;
|
||||
}
|
||||
static int GetCoeffsFast(VP8BitReader* const br,
|
||||
const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
const ProbaCtxArray p_ctx = prob[kBands[n + 1]];
|
||||
if (!VP8GetBit(br, p[1])) {
|
||||
p = p_ctx[0];
|
||||
} else { // non zero coeff
|
||||
if (!VP8GetBit(br, p[0])) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBit(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 (!VP8GetBit(br, p[2])) {
|
||||
v = 1;
|
||||
@@ -519,205 +456,212 @@ static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
||||
if (n < 15 && !VP8GetBit(br, p[0])) { // EOB
|
||||
return n + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
// Alias-safe way of converting 4bytes to 32bits.
|
||||
typedef union {
|
||||
uint8_t i8[4];
|
||||
uint32_t i32;
|
||||
} PackedNz;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Table to unpack four bits into four bytes
|
||||
static const PackedNz kUnpackTab[16] = {
|
||||
{{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
|
||||
{{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
|
||||
{{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
|
||||
{{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
|
||||
WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) {
|
||||
if (GetCoeffs == NULL) {
|
||||
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
|
||||
GetCoeffs = GetCoeffsAlt;
|
||||
} else {
|
||||
GetCoeffs = GetCoeffsFast;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Macro to pack four LSB of four bytes into four bits.
|
||||
#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
|
||||
defined(__BIG_ENDIAN__)
|
||||
#define PACK_CST 0x08040201U
|
||||
#else
|
||||
#define PACK_CST 0x01020408U
|
||||
#endif
|
||||
#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
|
||||
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
||||
nz_coeffs <<= 2;
|
||||
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
||||
return nz_coeffs;
|
||||
}
|
||||
|
||||
static void ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
int out_t_nz, out_l_nz, first;
|
||||
ProbaArray ac_prob;
|
||||
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
|
||||
int16_t* dst = dec->coeffs_;
|
||||
static int ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
||||
const VP8BandProbas* const * ac_proba;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
||||
int16_t* dst = block->coeffs_;
|
||||
VP8MB* const left_mb = dec->mb_info_ - 1;
|
||||
PackedNz nz_ac, nz_dc;
|
||||
PackedNz tnz, lnz;
|
||||
uint32_t non_zero_ac = 0;
|
||||
uint32_t non_zero_dc = 0;
|
||||
uint8_t tnz, lnz;
|
||||
uint32_t non_zero_y = 0;
|
||||
uint32_t non_zero_uv = 0;
|
||||
int x, y, ch;
|
||||
uint32_t out_t_nz, out_l_nz;
|
||||
int first;
|
||||
|
||||
nz_dc.i32 = nz_ac.i32 = 0;
|
||||
memset(dst, 0, 384 * sizeof(*dst));
|
||||
if (!dec->is_i4x4_) { // parse DC
|
||||
if (!block->is_i4x4_) { // parse DC
|
||||
int16_t dc[16] = { 0 };
|
||||
const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
|
||||
mb->dc_nz_ = left_mb->dc_nz_ =
|
||||
(GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
|
||||
ctx, q->y2_mat_, 0, dc) > 0);
|
||||
const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
|
||||
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
|
||||
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
|
||||
if (nz > 1) { // more than just the DC -> perform the full transform
|
||||
VP8TransformWHT(dc, dst);
|
||||
} else { // only DC is non-zero -> inlined simplified transform
|
||||
int i;
|
||||
const int dc0 = (dc[0] + 3) >> 3;
|
||||
for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
|
||||
}
|
||||
first = 1;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
|
||||
VP8TransformWHT(dc, dst);
|
||||
ac_proba = bands[0];
|
||||
} else {
|
||||
first = 0;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
|
||||
ac_proba = bands[3];
|
||||
}
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ & 0xf];
|
||||
lnz = kUnpackTab[left_mb->nz_ & 0xf];
|
||||
tnz = mb->nz_ & 0x0f;
|
||||
lnz = left_mb->nz_ & 0x0f;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int l = lnz.i8[y];
|
||||
int l = lnz & 1;
|
||||
uint32_t nz_coeffs = 0;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = l + tnz.i8[x];
|
||||
const int nz = GetCoeffs(token_br, ac_prob, ctx,
|
||||
q->y1_mat_, first, dst);
|
||||
tnz.i8[x] = l = (nz > 0);
|
||||
nz_dc.i8[x] = (dst[0] != 0);
|
||||
nz_ac.i8[x] = (nz > 1);
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
|
||||
l = (nz > first);
|
||||
tnz = (tnz >> 1) | (l << 7);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[y] = l;
|
||||
non_zero_dc |= PACK(nz_dc, 24 - y * 4);
|
||||
non_zero_ac |= PACK(nz_ac, 24 - y * 4);
|
||||
tnz >>= 4;
|
||||
lnz = (lnz >> 1) | (l << 7);
|
||||
non_zero_y = (non_zero_y << 8) | nz_coeffs;
|
||||
}
|
||||
out_t_nz = PACK(tnz, 24);
|
||||
out_l_nz = PACK(lnz, 24);
|
||||
out_t_nz = tnz;
|
||||
out_l_nz = lnz >> 4;
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ >> 4];
|
||||
lnz = kUnpackTab[left_mb->nz_ >> 4];
|
||||
for (ch = 0; ch < 4; ch += 2) {
|
||||
uint32_t nz_coeffs = 0;
|
||||
tnz = mb->nz_ >> (4 + ch);
|
||||
lnz = left_mb->nz_ >> (4 + ch);
|
||||
for (y = 0; y < 2; ++y) {
|
||||
int l = lnz.i8[ch + y];
|
||||
int l = lnz & 1;
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = l + tnz.i8[ch + x];
|
||||
const int nz =
|
||||
GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
|
||||
ctx, q->uv_mat_, 0, dst);
|
||||
tnz.i8[ch + x] = l = (nz > 0);
|
||||
nz_dc.i8[y * 2 + x] = (dst[0] != 0);
|
||||
nz_ac.i8[y * 2 + x] = (nz > 1);
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
|
||||
l = (nz > 0);
|
||||
tnz = (tnz >> 1) | (l << 3);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[ch + y] = l;
|
||||
tnz >>= 2;
|
||||
lnz = (lnz >> 1) | (l << 5);
|
||||
}
|
||||
non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
|
||||
non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
|
||||
// Note: we don't really need the per-4x4 details for U/V blocks.
|
||||
non_zero_uv |= nz_coeffs << (4 * ch);
|
||||
out_t_nz |= (tnz << 4) << ch;
|
||||
out_l_nz |= (lnz & 0xf0) << ch;
|
||||
}
|
||||
out_t_nz |= PACK(tnz, 20);
|
||||
out_l_nz |= PACK(lnz, 20);
|
||||
mb->nz_ = out_t_nz;
|
||||
left_mb->nz_ = out_l_nz;
|
||||
|
||||
dec->non_zero_ac_ = non_zero_ac;
|
||||
dec->non_zero_ = non_zero_ac | non_zero_dc;
|
||||
mb->skip_ = !dec->non_zero_;
|
||||
block->non_zero_y_ = non_zero_y;
|
||||
block->non_zero_uv_ = non_zero_uv;
|
||||
|
||||
// We look at the mode-code of each block and check if some blocks have less
|
||||
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
|
||||
// empty blocks.
|
||||
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
|
||||
|
||||
return !(non_zero_y | non_zero_uv); // will be used for further optimization
|
||||
}
|
||||
#undef PACK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main loop
|
||||
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
|
||||
VP8GetBit(br, dec->proba_.segments_[1]) :
|
||||
2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
}
|
||||
info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
|
||||
|
||||
VP8ParseIntraMode(br, dec);
|
||||
if (br->eof_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!info->skip_) {
|
||||
ParseResiduals(dec, info, token_br);
|
||||
if (!skip) {
|
||||
skip = ParseResiduals(dec, mb, token_br);
|
||||
} else {
|
||||
left->nz_ = info->nz_ = 0;
|
||||
if (!dec->is_i4x4_) {
|
||||
left->dc_nz_ = info->dc_nz_ = 0;
|
||||
left->nz_ = mb->nz_ = 0;
|
||||
if (!block->is_i4x4_) {
|
||||
left->nz_dc_ = mb->nz_dc_ = 0;
|
||||
}
|
||||
dec->non_zero_ = 0;
|
||||
dec->non_zero_ac_ = 0;
|
||||
block->non_zero_y_ = 0;
|
||||
block->non_zero_uv_ = 0;
|
||||
block->dither_ = 0;
|
||||
}
|
||||
|
||||
if (dec->filter_type_ > 0) { // store filter info
|
||||
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
|
||||
*finfo = dec->fstrengths_[dec->segment_][dec->is_i4x4_];
|
||||
finfo->f_inner_ = (!info->skip_ || dec->is_i4x4_);
|
||||
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
|
||||
finfo->f_inner_ |= !skip;
|
||||
}
|
||||
|
||||
return (!token_br->eof_);
|
||||
return !token_br->eof_;
|
||||
}
|
||||
|
||||
void VP8InitScanline(VP8Decoder* const dec) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
left->nz_ = 0;
|
||||
left->dc_nz_ = 0;
|
||||
left->nz_dc_ = 0;
|
||||
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
||||
dec->filter_row_ =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
|
||||
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||
// Parse bitstream for this row.
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
VP8InitScanline(dec);
|
||||
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-partition0 encountered.");
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-file encountered.");
|
||||
}
|
||||
// Reconstruct and emit samples.
|
||||
VP8ReconstructBlock(dec);
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
||||
}
|
||||
}
|
||||
if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
|
||||
return 0;
|
||||
if (dec->mt_method_ > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
|
||||
}
|
||||
|
||||
// Finish
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->update_proba_) {
|
||||
dec->proba_ = dec->proba_saved_;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (dec->layer_data_size_ > 0) {
|
||||
if (!VP8DecodeLayer(dec)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -765,12 +709,9 @@ void VP8Clear(VP8Decoder* const dec) {
|
||||
if (dec == NULL) {
|
||||
return;
|
||||
}
|
||||
if (dec->use_threads_) {
|
||||
WebPWorkerEnd(&dec->worker_);
|
||||
}
|
||||
if (dec->mem_) {
|
||||
free(dec->mem_);
|
||||
}
|
||||
WebPGetWorkerInterface()->End(&dec->worker_);
|
||||
WebPDeallocateAlphaMemory(dec);
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_ = NULL;
|
||||
dec->mem_size_ = 0;
|
||||
memset(&dec->br_, 0, sizeof(dec->br_));
|
||||
@@ -778,7 +719,3 @@ void VP8Clear(VP8Decoder* const dec) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "../webp/decode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -132,7 +132,8 @@ static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
|
||||
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Start decoding a new picture. Returns true if ok.
|
||||
// Decode the VP8 frame header. Returns true if ok.
|
||||
// Note: 'io->data' must be pointing to the start of the VP8 frame header.
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
|
||||
@@ -177,7 +178,7 @@ WEBP_EXTERN(int) VP8LGetInfo(
|
||||
const uint8_t* data, size_t data_size, // data available so far
|
||||
int* const width, int* const height, int* const has_alpha);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
+100
-116
@@ -15,12 +15,14 @@
|
||||
#define WEBP_DEC_VP8I_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/thread.h"
|
||||
#include "./common_dec.h"
|
||||
#include "./vp8li_dec.h"
|
||||
#include "../utils/bit_reader_utils.h"
|
||||
#include "../utils/random_utils.h"
|
||||
#include "../utils/thread_utils.h"
|
||||
#include "../dsp/dsp.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -29,48 +31,10 @@ extern "C" {
|
||||
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 0
|
||||
#define DEC_MIN_VERSION 3
|
||||
#define DEC_REV_VERSION 1
|
||||
#define DEC_MIN_VERSION 6
|
||||
#define DEC_REV_VERSION 0
|
||||
|
||||
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
|
||||
|
||||
// intra prediction modes
|
||||
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.
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// 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,
|
||||
// in order to be SIMD-friendly. We also need to store the top, left and
|
||||
@@ -92,14 +56,15 @@ enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
// 'y' = y-samples 'u' = u-samples 'v' = u-samples
|
||||
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
||||
// '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 Y_SIZE (BPS * 17)
|
||||
#define Y_OFF (BPS * 1 + 8)
|
||||
#define U_OFF (Y_OFF + BPS * 16 + BPS)
|
||||
#define V_OFF (U_OFF + 16)
|
||||
|
||||
// minimal width under which lossy multi-threading is always disabled
|
||||
#define MIN_WIDTH_FOR_THREADS 512
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Headers
|
||||
|
||||
@@ -128,15 +93,19 @@ typedef struct {
|
||||
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||
} VP8SegmentHeader;
|
||||
|
||||
// probas associated to one of the contexts
|
||||
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
|
||||
|
||||
typedef struct { // all the probas associated to one band
|
||||
VP8ProbaArray probas_[NUM_CTX];
|
||||
} VP8BandProbas;
|
||||
|
||||
// Struct collecting all frame-persistent probabilities.
|
||||
typedef struct {
|
||||
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
||||
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
||||
uint8_t coeffs_[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t ymode_[4], uvmode_[3];
|
||||
uint8_t mv_[2][NUM_MV_PROBAS];
|
||||
#endif
|
||||
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
|
||||
const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1];
|
||||
} VP8Proba;
|
||||
|
||||
// Filter parameters
|
||||
@@ -153,32 +122,61 @@ typedef struct {
|
||||
// Informations about the macroblocks.
|
||||
|
||||
typedef struct { // filter specs
|
||||
unsigned int f_level_:6; // filter strength: 0..63
|
||||
unsigned int f_ilevel_:6; // inner limit: 1..63
|
||||
unsigned int f_inner_:1; // do inner filtering?
|
||||
uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering
|
||||
uint8_t f_ilevel_; // inner limit in [1..63]
|
||||
uint8_t f_inner_; // do inner filtering?
|
||||
uint8_t hev_thresh_; // high edge variance threshold in [0..2]
|
||||
} VP8FInfo;
|
||||
|
||||
typedef struct { // used for syntax-parsing
|
||||
unsigned int nz_:24; // non-zero AC/DC coeffs (24bit)
|
||||
unsigned int dc_nz_:1; // non-zero DC coeffs
|
||||
unsigned int skip_:1; // block type
|
||||
typedef struct { // Top/Left Contexts used for syntax-parsing
|
||||
uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
|
||||
uint8_t nz_dc_; // non-zero DC coeff (1bit)
|
||||
} VP8MB;
|
||||
|
||||
// Dequantization matrices
|
||||
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
|
||||
typedef struct {
|
||||
quant_t y1_mat_, y2_mat_, uv_mat_;
|
||||
|
||||
int uv_quant_; // U/V quantizer value
|
||||
int dither_; // dithering amplitude (0 = off, max=255)
|
||||
} VP8QuantMatrix;
|
||||
|
||||
// Data needed to reconstruct a macroblock
|
||||
typedef struct {
|
||||
int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
// bit-wise info about the content of each sub-4x4 blocks (in decoding order).
|
||||
// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
|
||||
// code=0 -> no coefficient
|
||||
// code=1 -> only DC
|
||||
// code=2 -> first three coefficients are non-zero
|
||||
// code=3 -> more than three coefficients are non-zero
|
||||
// This allows to call specialized transform functions.
|
||||
uint32_t non_zero_y_;
|
||||
uint32_t non_zero_uv_;
|
||||
uint8_t dither_; // local dithering strength (deduced from non_zero_*)
|
||||
uint8_t skip_;
|
||||
uint8_t segment_;
|
||||
} VP8MBData;
|
||||
|
||||
// Persistent information needed by the parallel processing
|
||||
typedef struct {
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_)
|
||||
VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_)
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
} VP8ThreadContext;
|
||||
|
||||
// Saved top samples, per macroblock. Fits into a cache-line.
|
||||
typedef struct {
|
||||
uint8_t y[16], u[8], v[8];
|
||||
} VP8TopSamples;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder: the main opaque structure handed over to user
|
||||
|
||||
@@ -198,7 +196,8 @@ struct VP8Decoder {
|
||||
|
||||
// Worker
|
||||
WebPWorker worker_;
|
||||
int use_threads_; // use multi-thread
|
||||
int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter]
|
||||
// 2=[parse][recon+filter]
|
||||
int cache_id_; // current cache row
|
||||
int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
|
||||
VP8ThreadContext thread_ctx_; // Thread context
|
||||
@@ -210,17 +209,14 @@ struct VP8Decoder {
|
||||
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
|
||||
|
||||
// number of partitions.
|
||||
int num_parts_;
|
||||
// number of partitions minus one.
|
||||
uint32_t num_parts_minus_one_;
|
||||
// per-partition boolean decoders.
|
||||
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
||||
|
||||
// buffer refresh flags
|
||||
// bit 0: refresh Gold, bit 1: refresh Alt
|
||||
// bit 2-3: copy to Gold, bit 4-5: copy to Alt
|
||||
// bit 6: Gold sign bias, bit 7: Alt sign bias
|
||||
// bit 8: refresh last frame
|
||||
uint32_t buffer_flags_;
|
||||
// Dithering strength, deduced from decoding options
|
||||
int dither_; // whether to use dithering or not
|
||||
VP8Random dithering_rg_; // random generator for dithering
|
||||
|
||||
// dequantization (one set of DC/AC dequant factor per segment)
|
||||
VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
|
||||
@@ -229,24 +225,18 @@ struct VP8Decoder {
|
||||
VP8Proba proba_;
|
||||
int use_skip_proba_;
|
||||
uint8_t skip_p_;
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t intra_p_, last_p_, golden_p_;
|
||||
VP8Proba proba_saved_;
|
||||
int update_proba_;
|
||||
#endif
|
||||
|
||||
// Boundary data cache and persistent buffers.
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
uint8_t* y_t_; // top luma samples: 16 * mb_w_
|
||||
uint8_t* u_t_, *v_t_; // top u/v samples: 8 * mb_w_ each
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
int16_t* coeffs_; // 384 coeffs = (16+8+8) * 4*4
|
||||
VP8TopSamples* yuv_t_; // top y/u/v samples
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
uint8_t* cache_u_;
|
||||
uint8_t* cache_v_;
|
||||
int cache_y_stride_;
|
||||
@@ -258,32 +248,21 @@ struct VP8Decoder {
|
||||
|
||||
// Per macroblock non-persistent infos.
|
||||
int mb_x_, mb_y_; // current position, in macroblock units
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
uint8_t segment_; // block's segment
|
||||
|
||||
// bit-wise info about the content of each sub-4x4 blocks: there are 16 bits
|
||||
// for luma (bits #0->#15), then 4 bits for chroma-u (#16->#19) and 4 bits for
|
||||
// chroma-v (#20->#23), each corresponding to one 4x4 block in decoding order.
|
||||
// If the bit is set, the 4x4 block contains some non-zero coefficients.
|
||||
uint32_t non_zero_;
|
||||
uint32_t non_zero_ac_;
|
||||
VP8MBData* mb_data_; // parsed reconstruction data
|
||||
|
||||
// Filtering side-info
|
||||
int filter_type_; // 0=off, 1=simple, 2=complex
|
||||
int filter_row_; // per-row flag
|
||||
VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
|
||||
|
||||
// extensions
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
// Alpha
|
||||
struct ALPHDecoder* alph_dec_; // alpha-plane decoder object
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
size_t alpha_data_size_;
|
||||
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
|
||||
int layer_colorspace_;
|
||||
const uint8_t* layer_data_; // compressed layer data (if present)
|
||||
size_t layer_data_size_;
|
||||
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.
|
||||
const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL)
|
||||
int alpha_dithering_; // derived from decoding options (0=off, 100=full)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -296,15 +275,14 @@ int VP8SetError(VP8Decoder* const dec,
|
||||
// in tree.c
|
||||
void VP8ResetProba(VP8Proba* const proba);
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
// parses one row of intra mode data in partition 0, returns !eof
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
|
||||
// in quant.c
|
||||
void VP8ParseQuant(VP8Decoder* const dec);
|
||||
|
||||
// in frame.c
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
|
||||
// Predict a block and add residual
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec);
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Call io->setup() and finish setting up scan parameters.
|
||||
// After this call returns, one must always call VP8ExitCritical() with the
|
||||
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
||||
@@ -313,7 +291,15 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Must always be called in pair with VP8EnterCritical().
|
||||
// Returns false in case of error.
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Process the last decoded row (filtering + output)
|
||||
// Return the multi-threading method to use (0=off), depending
|
||||
// on options and bitstream size. Only for lossy decoding.
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height);
|
||||
// Initialize dithering post-process if needed.
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec);
|
||||
// Process the last decoded row (filtering + output).
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||
// To be called at the start of a new scanline, to initialize predictors.
|
||||
void VP8InitScanline(VP8Decoder* const dec);
|
||||
@@ -322,14 +308,12 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
|
||||
|
||||
// in alpha.c
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
const VP8Io* const io,
|
||||
int row, int num_rows);
|
||||
|
||||
// in layer.c
|
||||
int VP8DecodeLayer(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
Vendored
-1249
File diff suppressed because it is too large
Load Diff
Vendored
+1671
File diff suppressed because it is too large
Load Diff
+30
-19
@@ -16,13 +16,12 @@
|
||||
#define WEBP_DEC_VP8LI_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "./webpi.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/color_cache.h"
|
||||
#include "../utils/huffman.h"
|
||||
#include "../webp/format_constants.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "../utils/bit_reader_utils.h"
|
||||
#include "../utils/color_cache_utils.h"
|
||||
#include "../utils/huffman_utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -41,13 +40,10 @@ struct VP8LTransform {
|
||||
uint32_t *data_; // transform data.
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
|
||||
} HTreeGroup;
|
||||
|
||||
typedef struct {
|
||||
int color_cache_size_;
|
||||
VP8LColorCache color_cache_;
|
||||
VP8LColorCache saved_color_cache_; // for incremental
|
||||
|
||||
int huffman_mask_;
|
||||
int huffman_subsample_bits_;
|
||||
@@ -55,11 +51,12 @@ typedef struct {
|
||||
uint32_t *huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup *htree_groups_;
|
||||
HuffmanCode *huffman_tables_;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct {
|
||||
typedef struct VP8LDecoder VP8LDecoder;
|
||||
struct VP8LDecoder {
|
||||
VP8StatusCode status_;
|
||||
VP8LDecodeState action_;
|
||||
VP8LDecodeState state_;
|
||||
VP8Io *io_;
|
||||
|
||||
@@ -70,10 +67,16 @@ typedef struct {
|
||||
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||
|
||||
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 height_;
|
||||
int last_row_; // last input row decoded so far.
|
||||
int last_pixel_; // last pixel decoded so far. However, it may
|
||||
// not be transformed, scaled and
|
||||
// color-converted yet.
|
||||
int last_out_row_; // last row output so far.
|
||||
|
||||
VP8LMetadata hdr_;
|
||||
@@ -85,18 +88,26 @@ typedef struct {
|
||||
|
||||
uint8_t *rescaler_memory; // Working memory for rescaling work.
|
||||
WebPRescaler *rescaler; // Common rescaler for all channels.
|
||||
} VP8LDecoder;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
struct ALPHDecoder; // Defined in dec/alphai.h.
|
||||
|
||||
// in vp8l.c
|
||||
|
||||
// Decodes a raw image stream (without header) and store the alpha data
|
||||
// into *output, which must be of size width x height. Returns false in case
|
||||
// of error.
|
||||
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
|
||||
size_t data_size, uint8_t* const output);
|
||||
// Decodes image header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error.
|
||||
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const data, size_t data_size);
|
||||
|
||||
// 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
|
||||
// was paused.
|
||||
// Returns false in case of bitstream error.
|
||||
int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
|
||||
int last_row);
|
||||
|
||||
// Allocates and initialize a new lossless decoder instance.
|
||||
VP8LDecoder* VP8LNew(void);
|
||||
@@ -117,7 +128,7 @@ void VP8LDelete(VP8LDecoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
+125
-73
@@ -13,15 +13,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "./vp8i_dec.h"
|
||||
#include "./vp8li_dec.h"
|
||||
#include "./webpi_dec.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/mux_types.h" // ALPHA_FLAG
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RIFF layout is:
|
||||
// Offset tag
|
||||
@@ -42,27 +39,20 @@ extern "C" {
|
||||
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
|
||||
// 24..26 Width of the Canvas Image.
|
||||
// 27..29 Height of the Canvas Image.
|
||||
// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
|
||||
// VP8L, XMP, EXIF ...)
|
||||
// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L,
|
||||
// XMP, EXIF ...)
|
||||
// All sizes are in little-endian order.
|
||||
// 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.
|
||||
// If a RIFF container is detected,
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If a RIFF container is detected, returns:
|
||||
// VP8_STATUS_BITSTREAM_ERROR for invalid header,
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true,
|
||||
// and VP8_STATUS_OK otherwise.
|
||||
// In case there are not enough bytes (partial RIFF container), return 0 for
|
||||
// *riff_size. Else return the RIFF size extracted from the header.
|
||||
static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t* const riff_size) {
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
@@ -73,7 +63,7 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
|
||||
} 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").
|
||||
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
@@ -81,6 +71,9 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
if (size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
|
||||
}
|
||||
// We have a RIFF container. Skip it.
|
||||
*riff_size = size;
|
||||
*data += RIFF_HEADER_SIZE;
|
||||
@@ -116,7 +109,7 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
|
||||
int width, height;
|
||||
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) {
|
||||
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) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||
}
|
||||
flags = get_le32(*data + 8);
|
||||
width = 1 + get_le24(*data + 12);
|
||||
height = 1 + get_le24(*data + 15);
|
||||
flags = GetLE32(*data + 8);
|
||||
width = 1 + GetLE24(*data + 12);
|
||||
height = 1 + GetLE24(*data + 15);
|
||||
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
|
||||
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;
|
||||
}
|
||||
|
||||
chunk_size = get_le32(buf + TAG_SIZE);
|
||||
chunk_size = GetLE32(buf + TAG_SIZE);
|
||||
if (chunk_size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
}
|
||||
@@ -227,9 +220,8 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
// extracted from the VP8/VP8L chunk header.
|
||||
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
|
||||
static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
size_t* const data_size,
|
||||
size_t riff_size,
|
||||
size_t* const chunk_size,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t riff_size, size_t* const chunk_size,
|
||||
int* const is_lossless) {
|
||||
const uint8_t* const data = *data_ptr;
|
||||
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
|
||||
@@ -248,10 +240,13 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
|
||||
if (is_vp8 || is_vp8l) {
|
||||
// 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)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
|
||||
}
|
||||
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
|
||||
}
|
||||
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
|
||||
*chunk_size = size;
|
||||
*data_ptr += CHUNK_HEADER_SIZE;
|
||||
@@ -285,9 +280,17 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
int* const height,
|
||||
int* const has_alpha,
|
||||
int* const has_animation,
|
||||
int* const format,
|
||||
WebPHeaderStructure* const headers) {
|
||||
int canvas_width = 0;
|
||||
int canvas_height = 0;
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int found_riff = 0;
|
||||
int found_vp8x = 0;
|
||||
int animation_present = 0;
|
||||
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
|
||||
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure hdrs;
|
||||
|
||||
@@ -299,7 +302,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
hdrs.data_size = data_size;
|
||||
|
||||
// Skip over RIFF header.
|
||||
status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
|
||||
status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong RIFF header / insufficient data.
|
||||
}
|
||||
@@ -308,23 +311,33 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
// Skip over VP8X.
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x,
|
||||
&canvas_width, &canvas_height, &flags);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8X / insufficient data.
|
||||
}
|
||||
animation_present = !!(flags & ANIMATION_FLAG);
|
||||
if (!found_riff && found_vp8x) {
|
||||
// Note: This restriction may be removed in the future, if it becomes
|
||||
// necessary to send VP8X chunk to the decoder.
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
|
||||
if (has_animation != NULL) *has_animation = !!(flags & ANIMATION_FLAG);
|
||||
if (found_vp8x && headers == NULL) {
|
||||
return VP8_STATUS_OK; // Return features from VP8X header.
|
||||
if (has_animation != NULL) *has_animation = animation_present;
|
||||
if (format != NULL) *format = 0; // default = undefined
|
||||
|
||||
image_width = canvas_width;
|
||||
image_height = canvas_height;
|
||||
if (found_vp8x && animation_present && headers == NULL) {
|
||||
status = VP8_STATUS_OK;
|
||||
goto ReturnWidthHeight; // Just return features from VP8X header.
|
||||
}
|
||||
}
|
||||
|
||||
if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
if (data_size < TAG_SIZE) {
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
|
||||
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
|
||||
if ((found_riff && found_vp8x) ||
|
||||
@@ -332,43 +345,49 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
|
||||
&hdrs.alpha_data, &hdrs.alpha_data_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Found an invalid chunk size / insufficient data.
|
||||
goto ReturnWidthHeight; // Invalid chunk size / insufficient data.
|
||||
}
|
||||
}
|
||||
|
||||
// Skip over VP8/VP8L header.
|
||||
status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
|
||||
status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size,
|
||||
&hdrs.compressed_size, &hdrs.is_lossless);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8/VP8L chunk-header / insufficient data.
|
||||
goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data.
|
||||
}
|
||||
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
|
||||
if (format != NULL && !animation_present) {
|
||||
*format = hdrs.is_lossless ? 2 : 1;
|
||||
}
|
||||
|
||||
if (!hdrs.is_lossless) {
|
||||
if (data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
// Validates raw VP8 data.
|
||||
if (!VP8GetInfo(data, data_size,
|
||||
(uint32_t)hdrs.compressed_size, width, height)) {
|
||||
if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
|
||||
&image_width, &image_height)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (data_size < VP8L_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
// Validates raw VP8L data.
|
||||
if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
|
||||
if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_alpha != NULL) {
|
||||
// If the data did not contain a VP8X/VP8L chunk the only definitive way
|
||||
// to set this is by looking for alpha data (from an ALPH chunk).
|
||||
*has_alpha |= (hdrs.alpha_data != NULL);
|
||||
// Validates image size coherency.
|
||||
if (found_vp8x) {
|
||||
if (canvas_width != image_width || canvas_height != image_height) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
}
|
||||
if (headers != NULL) {
|
||||
*headers = hdrs;
|
||||
@@ -376,16 +395,31 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
|
||||
assert(headers->offset == headers->data_size - data_size);
|
||||
}
|
||||
return VP8_STATUS_OK; // Return features from VP8 header.
|
||||
ReturnWidthHeight:
|
||||
if (status == VP8_STATUS_OK ||
|
||||
(status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
|
||||
if (has_alpha != NULL) {
|
||||
// If the data did not contain a VP8X/VP8L chunk the only definitive way
|
||||
// to set this is by looking for alpha data (from an ALPH chunk).
|
||||
*has_alpha |= (hdrs.alpha_data != NULL);
|
||||
}
|
||||
if (width != NULL) *width = image_width;
|
||||
if (height != NULL) *height = image_height;
|
||||
return VP8_STATUS_OK;
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
assert(headers != NULL);
|
||||
// fill out headers, ignore width/height/has_alpha.
|
||||
status = ParseHeadersInternal(headers->data, headers->data_size,
|
||||
NULL, NULL, NULL, &has_animation, headers);
|
||||
NULL, NULL, NULL, &has_animation,
|
||||
NULL, headers);
|
||||
if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
// TODO(jzern): full support of animation frames will require API additions.
|
||||
if (has_animation) {
|
||||
@@ -399,7 +433,7 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||
// WebPDecParams
|
||||
|
||||
void WebPResetDecParams(WebPDecParams* const params) {
|
||||
if (params) {
|
||||
if (params != NULL) {
|
||||
memset(params, 0, sizeof(*params));
|
||||
}
|
||||
}
|
||||
@@ -416,6 +450,7 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = data_size;
|
||||
headers.have_all_data = 1;
|
||||
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status;
|
||||
@@ -432,11 +467,6 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = params->options && (params->options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
|
||||
@@ -448,6 +478,10 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
// This change must be done before calling VP8Decode()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
|
||||
io.width, io.height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
if (!VP8Decode(dec, &io)) {
|
||||
status = dec->status_;
|
||||
}
|
||||
@@ -476,6 +510,12 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
|
||||
if (status != VP8_STATUS_OK) {
|
||||
WebPFreeDecBuffer(params->output);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -634,7 +674,6 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
|
||||
assert(features != NULL);
|
||||
memset(features, 0, sizeof(*features));
|
||||
features->bitstream_version = 0;
|
||||
}
|
||||
|
||||
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
@@ -648,7 +687,7 @@ static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
return ParseHeadersInternal(data, data_size,
|
||||
&features->width, &features->height,
|
||||
&features->has_alpha, &features->has_animation,
|
||||
NULL);
|
||||
&features->format, NULL);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -719,9 +758,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
||||
}
|
||||
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &config->output;
|
||||
params.options = &config->options;
|
||||
status = DecodeInto(data, data_size, ¶ms);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -742,9 +796,9 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
h = options->crop_height;
|
||||
x = options->crop_left;
|
||||
y = options->crop_top;
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
|
||||
x &= ~1;
|
||||
y &= ~1; // TODO(later): only for YUV420, not YUV422.
|
||||
y &= ~1;
|
||||
}
|
||||
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
|
||||
return 0; // out of frame boundary error
|
||||
@@ -760,15 +814,17 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
// Scaling
|
||||
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
|
||||
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;
|
||||
}
|
||||
io->scaled_width = options->scaled_width;
|
||||
io->scaled_height = options->scaled_height;
|
||||
io->scaled_width = scaled_width;
|
||||
io->scaled_height = scaled_height;
|
||||
}
|
||||
|
||||
// Filter
|
||||
io->bypass_filtering = options && options->bypass_filtering;
|
||||
io->bypass_filtering = (options != NULL) && options->bypass_filtering;
|
||||
|
||||
// Fancy upsampler
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
@@ -785,7 +841,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -14,19 +14,22 @@
|
||||
#ifndef WEBP_DEC_WEBPI_H_
|
||||
#define WEBP_DEC_WEBPI_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../utils/rescaler.h"
|
||||
#include "./decode_vp8.h"
|
||||
#include "../utils/rescaler_utils.h"
|
||||
#include "./vp8_dec.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams: Decoding output parameters. Transient internal object.
|
||||
|
||||
typedef struct WebPDecParams WebPDecParams;
|
||||
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 {
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
@@ -35,12 +38,12 @@ struct WebPDecParams {
|
||||
|
||||
int last_y; // coordinate of the line that was last output
|
||||
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.
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
@@ -54,6 +57,7 @@ void WebPResetDecParams(WebPDecParams* const params);
|
||||
typedef struct {
|
||||
const uint8_t* data; // input buffer
|
||||
size_t data_size; // input buffer size
|
||||
int have_all_data; // true if all data is known to be available
|
||||
size_t offset; // offset to main data chunk (VP8 or VP8L)
|
||||
const uint8_t* alpha_data; // points to alpha chunk (if present)
|
||||
size_t alpha_data_size; // alpha chunk size
|
||||
@@ -93,23 +97,36 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
// dimension / etc.). If *options is not NULL, also verify that the options'
|
||||
// parameters are valid and apply them to the width/height dimensions of the
|
||||
// output buffer. This takes cropping / scaling / rotation into account.
|
||||
// Also incorporates the options->flip flag to flip the buffer parameters if
|
||||
// needed.
|
||||
VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const buffer);
|
||||
|
||||
// Flip buffer vertically by negating the various strides.
|
||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
|
||||
|
||||
// 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,
|
||||
WebPDecBuffer* const dst);
|
||||
|
||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||
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);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
lib_LTLIBRARIES = libwebpdemux.la
|
||||
|
||||
libwebpdemux_la_SOURCES =
|
||||
libwebpdemux_la_SOURCES += anim_decode.c demux.c
|
||||
|
||||
libwebpdemuxinclude_HEADERS =
|
||||
libwebpdemuxinclude_HEADERS += ../webp/demux.h
|
||||
libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
|
||||
libwebpdemuxinclude_HEADERS += ../webp/types.h
|
||||
|
||||
libwebpdemux_la_LIBADD = ../libwebp.la
|
||||
libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:2:0
|
||||
libwebpdemuxincludedir = $(includedir)/webp
|
||||
pkgconfig_DATA = libwebpdemux.pc
|
||||
Vendored
+454
@@ -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);
|
||||
}
|
||||
}
|
||||
Vendored
+204
-193
@@ -11,7 +11,7 @@
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../webp/config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
@@ -23,13 +23,9 @@
|
||||
#include "../webp/demux.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DMUX_MAJ_VERSION 0
|
||||
#define DMUX_MIN_VERSION 1
|
||||
#define DMUX_REV_VERSION 1
|
||||
#define DMUX_MIN_VERSION 3
|
||||
#define DMUX_REV_VERSION 2
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
@@ -47,10 +43,11 @@ typedef struct {
|
||||
typedef struct Frame {
|
||||
int x_offset_, y_offset_;
|
||||
int width_, height_;
|
||||
int has_alpha_;
|
||||
int duration_;
|
||||
WebPMuxAnimDispose dispose_method_;
|
||||
int is_fragment_; // this is a frame fragment (and not a full frame).
|
||||
int frame_num_; // the referent frame number for use in assembling fragments.
|
||||
WebPMuxAnimBlend blend_method_;
|
||||
int frame_num_;
|
||||
int complete_; // img_components_ contains a full image.
|
||||
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
|
||||
struct Frame* next_;
|
||||
@@ -73,6 +70,7 @@ struct WebPDemuxer {
|
||||
Frame* frames_;
|
||||
Frame** frames_tail_;
|
||||
Chunk* chunks_; // non-image chunks
|
||||
Chunk** chunks_tail_;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -177,10 +175,9 @@ static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
|
||||
// Secondary chunk parsing
|
||||
|
||||
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
|
||||
Chunk** c = &dmux->chunks_;
|
||||
while (*c != NULL) c = &(*c)->next_;
|
||||
*c = chunk;
|
||||
*dmux->chunks_tail_ = chunk;
|
||||
chunk->next_ = NULL;
|
||||
dmux->chunks_tail_ = &chunk->next_;
|
||||
}
|
||||
|
||||
// Add a frame to the end of the list, ensuring the last frame is complete.
|
||||
@@ -195,19 +192,27 @@ static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
|
||||
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'.
|
||||
// If 'has_vp8l_alpha' is not NULL, it will be set to true if the frame is a
|
||||
// lossless image with alpha.
|
||||
static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
MemBuffer* const mem, Frame* const frame,
|
||||
int* const has_vp8l_alpha) {
|
||||
MemBuffer* const mem, Frame* const frame) {
|
||||
int alpha_chunks = 0;
|
||||
int image_chunks = 0;
|
||||
int done = (MemDataSize(mem) < min_size);
|
||||
ParseStatus status = PARSE_OK;
|
||||
|
||||
if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0; // Default.
|
||||
|
||||
if (done) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
do {
|
||||
@@ -229,6 +234,7 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
++alpha_chunks;
|
||||
frame->img_components_[1].offset_ = chunk_start_offset;
|
||||
frame->img_components_[1].size_ = chunk_size;
|
||||
frame->has_alpha_ = 1;
|
||||
frame->frame_num_ = frame_num;
|
||||
Skip(mem, payload_available);
|
||||
} else {
|
||||
@@ -254,13 +260,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
++image_chunks;
|
||||
frame->img_components_[0].offset_ = chunk_start_offset;
|
||||
frame->img_components_[0].size_ = chunk_size;
|
||||
frame->width_ = features.width;
|
||||
frame->height_ = features.height;
|
||||
if (has_vp8l_alpha != NULL) *has_vp8l_alpha = features.has_alpha;
|
||||
frame->frame_num_ = frame_num;
|
||||
frame->complete_ = (status == PARSE_OK);
|
||||
SetFrameInfo(chunk_start_offset, chunk_size, frame_num,
|
||||
status == PARSE_OK, &features, frame);
|
||||
Skip(mem, payload_available);
|
||||
} else {
|
||||
goto Done;
|
||||
@@ -295,7 +296,7 @@ static ParseStatus NewFrame(const MemBuffer* const mem,
|
||||
if (actual_size < min_size) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
*frame = (Frame*)calloc(1, sizeof(**frame));
|
||||
*frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
|
||||
return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
|
||||
}
|
||||
|
||||
@@ -303,9 +304,10 @@ static ParseStatus NewFrame(const MemBuffer* const mem,
|
||||
// 'frame_chunk_size' is the previously validated, padded chunk size.
|
||||
static ParseStatus ParseAnimationFrame(
|
||||
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
|
||||
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
|
||||
int added_frame = 0;
|
||||
int bits;
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
Frame* frame;
|
||||
ParseStatus status =
|
||||
@@ -317,17 +319,19 @@ static ParseStatus ParseAnimationFrame(
|
||||
frame->width_ = 1 + ReadLE24s(mem);
|
||||
frame->height_ = 1 + ReadLE24s(mem);
|
||||
frame->duration_ = ReadLE24s(mem);
|
||||
frame->dispose_method_ = (WebPMuxAnimDispose)(ReadByte(mem) & 1);
|
||||
bits = ReadByte(mem);
|
||||
frame->dispose_method_ =
|
||||
(bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
|
||||
frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
|
||||
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
|
||||
free(frame);
|
||||
WebPSafeFree(frame);
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
// Store a frame only if the animation flag is set there is some data for
|
||||
// this frame is available.
|
||||
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame,
|
||||
NULL);
|
||||
if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
|
||||
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
|
||||
if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
|
||||
added_frame = AddFrame(dmux, frame);
|
||||
if (added_frame) {
|
||||
++dmux->num_frames_;
|
||||
@@ -336,53 +340,17 @@ static ParseStatus ParseAnimationFrame(
|
||||
}
|
||||
}
|
||||
|
||||
if (!added_frame) free(frame);
|
||||
if (!added_frame) WebPSafeFree(frame);
|
||||
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 has_fragments = !!(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 there is some data for
|
||||
// this fragment is available.
|
||||
status = StoreFrame(frame_num, frgm_payload_size, mem, frame, NULL);
|
||||
if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) {
|
||||
added_fragment = AddFrame(dmux, frame);
|
||||
if (!added_fragment) {
|
||||
status = PARSE_ERROR;
|
||||
} else {
|
||||
dmux->num_frames_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added_fragment) free(frame);
|
||||
return status;
|
||||
}
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
// General chunk storage, starting with the header at 'start_offset', allowing
|
||||
// the user to request the payload via a fourcc string. 'size' includes the
|
||||
// header and the unpadded payload size.
|
||||
// Returns true on success, false otherwise.
|
||||
static int StoreChunk(WebPDemuxer* const dmux,
|
||||
size_t start_offset, uint32_t size) {
|
||||
Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk));
|
||||
Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
|
||||
if (chunk == NULL) return 0;
|
||||
|
||||
chunk->data_.offset_ = start_offset;
|
||||
@@ -394,20 +362,20 @@ static int StoreChunk(WebPDemuxer* const dmux,
|
||||
// -----------------------------------------------------------------------------
|
||||
// Primary chunk parsing
|
||||
|
||||
static int ReadHeader(MemBuffer* const mem) {
|
||||
static ParseStatus ReadHeader(MemBuffer* const mem) {
|
||||
const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
|
||||
uint32_t riff_size;
|
||||
|
||||
// Basic file level validation.
|
||||
if (MemDataSize(mem) < min_size) return 0;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
|
||||
memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
|
||||
return 0;
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
|
||||
if (riff_size < CHUNK_HEADER_SIZE) return 0;
|
||||
if (riff_size > MAX_CHUNK_PAYLOAD) return 0;
|
||||
if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
|
||||
if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
|
||||
|
||||
// There's no point in reading past the end of the RIFF chunk
|
||||
mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
|
||||
@@ -416,7 +384,7 @@ static int ReadHeader(MemBuffer* const mem) {
|
||||
}
|
||||
|
||||
Skip(mem, RIFF_HEADER_SIZE);
|
||||
return 1;
|
||||
return PARSE_OK;
|
||||
}
|
||||
|
||||
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
@@ -424,25 +392,25 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
Frame* frame;
|
||||
ParseStatus status;
|
||||
int has_vp8l_alpha = 0; // Frame contains a lossless image with alpha.
|
||||
int image_added = 0;
|
||||
|
||||
if (dmux->frames_ != NULL) return PARSE_ERROR;
|
||||
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
frame = (Frame*)calloc(1, sizeof(*frame));
|
||||
frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
|
||||
if (frame == NULL) return PARSE_ERROR;
|
||||
|
||||
// For the single image case we allow parsing of a partial frame, but we need
|
||||
// at least CHUNK_HEADER_SIZE for parsing.
|
||||
status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame,
|
||||
&has_vp8l_alpha);
|
||||
status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame);
|
||||
if (status != PARSE_ERROR) {
|
||||
const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
|
||||
// Clear any alpha when the alpha flag is missing.
|
||||
if (!has_alpha && frame->img_components_[1].size_ > 0) {
|
||||
frame->img_components_[1].offset_ = 0;
|
||||
frame->img_components_[1].size_ = 0;
|
||||
frame->has_alpha_ = 0;
|
||||
}
|
||||
|
||||
// Use the frame width/height as the canvas values for non-vp8x files.
|
||||
@@ -451,47 +419,26 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
||||
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
|
||||
dmux->canvas_width_ = frame->width_;
|
||||
dmux->canvas_height_ = frame->height_;
|
||||
dmux->feature_flags_ |= has_vp8l_alpha ? ALPHA_FLAG : 0;
|
||||
dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
|
||||
}
|
||||
if (!AddFrame(dmux, frame)) {
|
||||
status = PARSE_ERROR; // last frame was left incomplete
|
||||
} else {
|
||||
image_added = 1;
|
||||
dmux->num_frames_ = 1;
|
||||
}
|
||||
AddFrame(dmux, frame);
|
||||
dmux->num_frames_ = 1;
|
||||
} else {
|
||||
free(frame);
|
||||
}
|
||||
|
||||
if (!image_added) WebPSafeFree(frame);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
int anim_chunks = 0;
|
||||
uint32_t vp8x_size;
|
||||
ParseStatus status = PARSE_OK;
|
||||
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->is_ext_format_ = 1;
|
||||
Skip(mem, TAG_SIZE); // VP8X
|
||||
vp8x_size = ReadLE32(mem);
|
||||
if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
|
||||
if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
|
||||
vp8x_size += vp8x_size & 1;
|
||||
if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->feature_flags_ = ReadByte(mem);
|
||||
Skip(mem, 3); // Reserved.
|
||||
dmux->canvas_width_ = 1 + ReadLE24s(mem);
|
||||
dmux->canvas_height_ = 1 + ReadLE24s(mem);
|
||||
if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
|
||||
return PARSE_ERROR; // image final dimension is too large
|
||||
}
|
||||
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
|
||||
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
|
||||
|
||||
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
do {
|
||||
int store_chunk = 1;
|
||||
const size_t chunk_start_offset = mem->start_;
|
||||
@@ -510,7 +457,7 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
case MKFOURCC('V', 'P', '8', ' '):
|
||||
case MKFOURCC('V', 'P', '8', 'L'): {
|
||||
// check that this isn't an animation (all frames should be in an ANMF).
|
||||
if (anim_chunks > 0) return PARSE_ERROR;
|
||||
if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
|
||||
|
||||
Rewind(mem, CHUNK_HEADER_SIZE);
|
||||
status = ParseSingleImage(dmux);
|
||||
@@ -537,24 +484,18 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
status = ParseAnimationFrame(dmux, chunk_size_padded);
|
||||
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'): {
|
||||
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
case MKFOURCC('X', 'M', 'P', ' '): {
|
||||
store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
case MKFOURCC('E', 'X', 'I', 'F'): {
|
||||
store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
case MKFOURCC('X', 'M', 'P', ' '): {
|
||||
store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
|
||||
goto Skip;
|
||||
}
|
||||
Skip:
|
||||
default: {
|
||||
if (chunk_size_padded <= MemDataSize(mem)) {
|
||||
@@ -583,6 +524,37 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
return status;
|
||||
}
|
||||
|
||||
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||
MemBuffer* const mem = &dmux->mem_;
|
||||
uint32_t vp8x_size;
|
||||
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->is_ext_format_ = 1;
|
||||
Skip(mem, TAG_SIZE); // VP8X
|
||||
vp8x_size = ReadLE32(mem);
|
||||
if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
|
||||
if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
|
||||
vp8x_size += vp8x_size & 1;
|
||||
if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
dmux->feature_flags_ = ReadByte(mem);
|
||||
Skip(mem, 3); // Reserved.
|
||||
dmux->canvas_width_ = 1 + ReadLE24s(mem);
|
||||
dmux->canvas_height_ = 1 + ReadLE24s(mem);
|
||||
if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
|
||||
return PARSE_ERROR; // image final dimension is too large
|
||||
}
|
||||
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
|
||||
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
|
||||
|
||||
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
|
||||
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
|
||||
|
||||
return ParseVP8XChunks(dmux);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Format validation
|
||||
|
||||
@@ -597,30 +569,47 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 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.
|
||||
static int CheckFrameBounds(const Frame* const frame, int exact,
|
||||
int canvas_width, int canvas_height) {
|
||||
if (exact) {
|
||||
if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
|
||||
if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
|
||||
if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
||||
const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
|
||||
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
const Frame* f;
|
||||
const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||
const Frame* f = dmux->frames_;
|
||||
|
||||
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
|
||||
|
||||
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
|
||||
if (dmux->loop_count_ < 0) return 0;
|
||||
if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
|
||||
if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
|
||||
|
||||
for (f = dmux->frames_; f != NULL; f = f->next_) {
|
||||
while (f != NULL) {
|
||||
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
|
||||
// each fragment came from a fragment.
|
||||
// Check frame properties.
|
||||
for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
|
||||
const ChunkData* const image = f->img_components_;
|
||||
const ChunkData* const alpha = f->img_components_ + 1;
|
||||
|
||||
if (!has_fragments && f->is_fragment_) return 0;
|
||||
if (!has_frames && f->frame_num_ > 1) return 0;
|
||||
if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0;
|
||||
if (!is_animation && f->frame_num_ > 1) return 0;
|
||||
|
||||
if (f->complete_) {
|
||||
if (alpha->size_ == 0 && image->size_ == 0) return 0;
|
||||
// Ensure alpha precedes image bitstream.
|
||||
@@ -642,12 +631,14 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
||||
if (f->next_ != NULL) return 0;
|
||||
}
|
||||
|
||||
fragment_count += f->is_fragment_;
|
||||
if (f->width_ > 0 && f->height_ > 0 &&
|
||||
!CheckFrameBounds(f, !is_animation,
|
||||
dmux->canvas_width_, dmux->canvas_height_)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
++frame_count;
|
||||
}
|
||||
if (!has_fragments && frame_count > 1) return 0;
|
||||
if (fragment_count > 0 && frame_count != fragment_count) return 0;
|
||||
if (f == NULL) break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -662,9 +653,45 @@ static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
|
||||
dmux->canvas_width_ = -1;
|
||||
dmux->canvas_height_ = -1;
|
||||
dmux->frames_tail_ = &dmux->frames_;
|
||||
dmux->chunks_tail_ = &dmux->chunks_;
|
||||
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,
|
||||
WebPDemuxState* state, int version) {
|
||||
const ChunkParser* parser;
|
||||
@@ -673,29 +700,49 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
|
||||
MemBuffer mem;
|
||||
WebPDemuxer* dmux;
|
||||
|
||||
if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
|
||||
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
|
||||
if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
|
||||
|
||||
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
|
||||
if (!ReadHeader(&mem)) return NULL;
|
||||
status = ReadHeader(&mem);
|
||||
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) {
|
||||
*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
|
||||
: WEBP_DEMUX_PARSE_ERROR;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
partial = (mem.buf_size_ < mem.riff_end_);
|
||||
if (!allow_partial && partial) return NULL;
|
||||
|
||||
dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
|
||||
dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
|
||||
if (dmux == NULL) return NULL;
|
||||
InitDemux(dmux, &mem);
|
||||
|
||||
status = PARSE_ERROR;
|
||||
for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
|
||||
if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
|
||||
status = parser->parse(dmux);
|
||||
if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
|
||||
if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
|
||||
if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
|
||||
if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state) *state = dmux->state_;
|
||||
if (state != NULL) *state = dmux->state_;
|
||||
|
||||
if (status == PARSE_ERROR) {
|
||||
WebPDemuxDelete(dmux);
|
||||
@@ -712,14 +759,14 @@ void WebPDemuxDelete(WebPDemuxer* dmux) {
|
||||
for (f = dmux->frames_; f != NULL;) {
|
||||
Frame* const cur_frame = f;
|
||||
f = f->next_;
|
||||
free(cur_frame);
|
||||
WebPSafeFree(cur_frame);
|
||||
}
|
||||
for (c = dmux->chunks_; c != NULL;) {
|
||||
Chunk* const cur_chunk = c;
|
||||
c = c->next_;
|
||||
free(cur_chunk);
|
||||
WebPSafeFree(cur_chunk);
|
||||
}
|
||||
free(dmux);
|
||||
WebPSafeFree(dmux);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -741,8 +788,6 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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) {
|
||||
const Frame* f;
|
||||
for (f = dmux->frames_; f != NULL; f = f->next_) {
|
||||
@@ -751,21 +796,6 @@ static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
|
||||
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,
|
||||
const Frame* const frame,
|
||||
size_t* const data_size) {
|
||||
@@ -792,32 +822,27 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
|
||||
|
||||
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
|
||||
static int SynthesizeFrame(const WebPDemuxer* const dmux,
|
||||
const Frame* const first_frame,
|
||||
int fragment_num, WebPIterator* const iter) {
|
||||
const Frame* const frame,
|
||||
WebPIterator* const iter) {
|
||||
const uint8_t* const mem_buf = dmux->mem_.buf_;
|
||||
int num_fragments;
|
||||
size_t payload_size = 0;
|
||||
const Frame* const fragment =
|
||||
GetFragment(first_frame, fragment_num, &num_fragments);
|
||||
const uint8_t* const payload =
|
||||
GetFramePayload(mem_buf, fragment, &payload_size);
|
||||
const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
|
||||
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->fragment_num = fragment_num;
|
||||
iter->num_fragments = num_fragments;
|
||||
iter->x_offset = fragment->x_offset_;
|
||||
iter->y_offset = fragment->y_offset_;
|
||||
iter->width = fragment->width_;
|
||||
iter->height = fragment->height_;
|
||||
iter->duration = fragment->duration_;
|
||||
iter->dispose_method = fragment->dispose_method_;
|
||||
iter->complete = fragment->complete_;
|
||||
iter->x_offset = frame->x_offset_;
|
||||
iter->y_offset = frame->y_offset_;
|
||||
iter->width = frame->width_;
|
||||
iter->height = frame->height_;
|
||||
iter->has_alpha = frame->has_alpha_;
|
||||
iter->duration = frame->duration_;
|
||||
iter->dispose_method = frame->dispose_method_;
|
||||
iter->blend_method = frame->blend_method_;
|
||||
iter->complete = frame->complete_;
|
||||
iter->fragment.bytes = payload;
|
||||
iter->fragment.size = payload_size;
|
||||
// TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -831,7 +856,7 @@ static int SetFrame(int frame_num, WebPIterator* const iter) {
|
||||
frame = GetFrame(dmux, frame_num);
|
||||
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) {
|
||||
@@ -853,17 +878,6 @@ int WebPDemuxPrevFrame(WebPIterator* 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)iter;
|
||||
}
|
||||
@@ -949,6 +963,3 @@ void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
|
||||
(void)iter;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libwebpdemux
|
||||
Description: Library for parsing the WebP graphics format container
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: libwebp >= 0.2.0
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -lwebpdemux
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,3,0,2
|
||||
PRODUCTVERSION 0,3,0,2
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Google, Inc."
|
||||
VALUE "FileDescription", "libwebpdemux DLL"
|
||||
VALUE "FileVersion", "0.3.2"
|
||||
VALUE "InternalName", "libwebpdemux.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2017"
|
||||
VALUE "OriginalFilename", "libwebpdemux.dll"
|
||||
VALUE "ProductName", "WebP Image Demuxer"
|
||||
VALUE "ProductVersion", "0.3.2"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
noinst_LTLIBRARIES = libwebpdsp.la libwebpdsp_avx2.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_sse2.la libwebpdspdecode_sse2.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_sse41.la libwebpdspdecode_sse41.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_neon.la libwebpdspdecode_neon.la
|
||||
noinst_LTLIBRARIES += libwebpdsp_msa.la libwebpdspdecode_msa.la
|
||||
|
||||
if BUILD_LIBWEBPDECODER
|
||||
noinst_LTLIBRARIES += libwebpdspdecode.la
|
||||
endif
|
||||
|
||||
common_HEADERS = ../webp/types.h
|
||||
commondir = $(includedir)/webp
|
||||
|
||||
COMMON_SOURCES =
|
||||
COMMON_SOURCES += alpha_processing.c
|
||||
COMMON_SOURCES += alpha_processing_mips_dsp_r2.c
|
||||
COMMON_SOURCES += common_sse2.h
|
||||
COMMON_SOURCES += cpu.c
|
||||
COMMON_SOURCES += dec.c
|
||||
COMMON_SOURCES += dec_clip_tables.c
|
||||
COMMON_SOURCES += dec_mips32.c
|
||||
COMMON_SOURCES += dec_mips_dsp_r2.c
|
||||
COMMON_SOURCES += dsp.h
|
||||
COMMON_SOURCES += filters.c
|
||||
COMMON_SOURCES += filters_mips_dsp_r2.c
|
||||
COMMON_SOURCES += lossless.c
|
||||
COMMON_SOURCES += lossless.h
|
||||
COMMON_SOURCES += lossless_common.h
|
||||
COMMON_SOURCES += lossless_mips_dsp_r2.c
|
||||
COMMON_SOURCES += mips_macro.h
|
||||
COMMON_SOURCES += rescaler.c
|
||||
COMMON_SOURCES += rescaler_mips32.c
|
||||
COMMON_SOURCES += rescaler_mips_dsp_r2.c
|
||||
COMMON_SOURCES += upsampling.c
|
||||
COMMON_SOURCES += upsampling_mips_dsp_r2.c
|
||||
COMMON_SOURCES += yuv.c
|
||||
COMMON_SOURCES += yuv.h
|
||||
COMMON_SOURCES += yuv_mips32.c
|
||||
COMMON_SOURCES += yuv_mips_dsp_r2.c
|
||||
|
||||
ENC_SOURCES =
|
||||
ENC_SOURCES += argb.c
|
||||
ENC_SOURCES += argb_mips_dsp_r2.c
|
||||
ENC_SOURCES += cost.c
|
||||
ENC_SOURCES += cost_mips32.c
|
||||
ENC_SOURCES += cost_mips_dsp_r2.c
|
||||
ENC_SOURCES += enc.c
|
||||
ENC_SOURCES += enc_mips32.c
|
||||
ENC_SOURCES += enc_mips_dsp_r2.c
|
||||
ENC_SOURCES += lossless_enc.c
|
||||
ENC_SOURCES += lossless_enc_mips32.c
|
||||
ENC_SOURCES += lossless_enc_mips_dsp_r2.c
|
||||
|
||||
libwebpdsp_avx2_la_SOURCES =
|
||||
libwebpdsp_avx2_la_SOURCES += enc_avx2.c
|
||||
libwebpdsp_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
|
||||
|
||||
libwebpdspdecode_sse41_la_SOURCES =
|
||||
libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c
|
||||
libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c
|
||||
libwebpdspdecode_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdspdecode_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
|
||||
|
||||
libwebpdspdecode_sse2_la_SOURCES =
|
||||
libwebpdspdecode_sse2_la_SOURCES += alpha_processing_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += dec_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += filters_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += lossless_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += rescaler_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += upsampling_sse2.c
|
||||
libwebpdspdecode_sse2_la_SOURCES += yuv_sse2.c
|
||||
libwebpdspdecode_sse2_la_CPPFLAGS = $(libwebpdsp_sse2_la_CPPFLAGS)
|
||||
libwebpdspdecode_sse2_la_CFLAGS = $(libwebpdsp_sse2_la_CFLAGS)
|
||||
|
||||
libwebpdspdecode_neon_la_SOURCES =
|
||||
libwebpdspdecode_neon_la_SOURCES += alpha_processing_neon.c
|
||||
libwebpdspdecode_neon_la_SOURCES += dec_neon.c
|
||||
libwebpdspdecode_neon_la_SOURCES += filters_neon.c
|
||||
libwebpdspdecode_neon_la_SOURCES += lossless_neon.c
|
||||
libwebpdspdecode_neon_la_SOURCES += neon.h
|
||||
libwebpdspdecode_neon_la_SOURCES += rescaler_neon.c
|
||||
libwebpdspdecode_neon_la_SOURCES += upsampling_neon.c
|
||||
libwebpdspdecode_neon_la_CPPFLAGS = $(libwebpdsp_neon_la_CPPFLAGS)
|
||||
libwebpdspdecode_neon_la_CFLAGS = $(libwebpdsp_neon_la_CFLAGS)
|
||||
|
||||
libwebpdspdecode_msa_la_SOURCES =
|
||||
libwebpdspdecode_msa_la_SOURCES += dec_msa.c
|
||||
libwebpdspdecode_msa_la_SOURCES += filters_msa.c
|
||||
libwebpdspdecode_msa_la_SOURCES += lossless_msa.c
|
||||
libwebpdspdecode_msa_la_SOURCES += msa_macro.h
|
||||
libwebpdspdecode_msa_la_SOURCES += rescaler_msa.c
|
||||
libwebpdspdecode_msa_la_SOURCES += upsampling_msa.c
|
||||
libwebpdspdecode_msa_la_CPPFLAGS = $(libwebpdsp_msa_la_CPPFLAGS)
|
||||
libwebpdspdecode_msa_la_CFLAGS = $(libwebpdsp_msa_la_CFLAGS)
|
||||
|
||||
libwebpdsp_sse2_la_SOURCES =
|
||||
libwebpdsp_sse2_la_SOURCES += argb_sse2.c
|
||||
libwebpdsp_sse2_la_SOURCES += cost_sse2.c
|
||||
libwebpdsp_sse2_la_SOURCES += enc_sse2.c
|
||||
libwebpdsp_sse2_la_SOURCES += lossless_enc_sse2.c
|
||||
libwebpdsp_sse2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
|
||||
libwebpdsp_sse2_la_LIBADD = libwebpdspdecode_sse2.la
|
||||
|
||||
libwebpdsp_sse41_la_SOURCES =
|
||||
libwebpdsp_sse41_la_SOURCES += enc_sse41.c
|
||||
libwebpdsp_sse41_la_SOURCES += lossless_enc_sse41.c
|
||||
libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
|
||||
libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
|
||||
|
||||
libwebpdsp_neon_la_SOURCES =
|
||||
libwebpdsp_neon_la_SOURCES += enc_neon.c
|
||||
libwebpdsp_neon_la_SOURCES += lossless_enc_neon.c
|
||||
libwebpdsp_neon_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
|
||||
libwebpdsp_neon_la_LIBADD = libwebpdspdecode_neon.la
|
||||
|
||||
libwebpdsp_msa_la_SOURCES =
|
||||
libwebpdsp_msa_la_SOURCES += enc_msa.c
|
||||
libwebpdsp_msa_la_SOURCES += lossless_enc_msa.c
|
||||
libwebpdsp_msa_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdsp_msa_la_CFLAGS = $(AM_CFLAGS)
|
||||
libwebpdsp_msa_la_LIBADD = libwebpdspdecode_msa.la
|
||||
|
||||
libwebpdsp_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES)
|
||||
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += ../dec/vp8_dec.h
|
||||
noinst_HEADERS += ../webp/decode.h
|
||||
|
||||
libwebpdsp_la_CPPFLAGS =
|
||||
libwebpdsp_la_CPPFLAGS += $(AM_CPPFLAGS)
|
||||
libwebpdsp_la_CPPFLAGS += $(USE_EXPERIMENTAL_CODE) $(USE_SWAP_16BIT_CSP)
|
||||
libwebpdsp_la_LDFLAGS = -lm
|
||||
libwebpdsp_la_LIBADD =
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_avx2.la libwebpdsp_sse2.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_sse41.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_neon.la
|
||||
libwebpdsp_la_LIBADD += libwebpdsp_msa.la
|
||||
|
||||
if BUILD_LIBWEBPDECODER
|
||||
libwebpdspdecode_la_SOURCES = $(COMMON_SOURCES)
|
||||
|
||||
libwebpdspdecode_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
|
||||
libwebpdspdecode_la_LDFLAGS = $(libwebpdsp_la_LDFLAGS)
|
||||
libwebpdspdecode_la_LIBADD =
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la
|
||||
libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la
|
||||
endif
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
// 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for processing transparent channel.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include "./dsp.h"
|
||||
|
||||
// Tables can be faster on some platform but incur some extra binary size (~2k).
|
||||
// #define USE_TABLES_FOR_ALPHA_MULT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||
#define HALF ((1u << MFIX) >> 1)
|
||||
#define KINV_255 ((1u << MFIX) / 255u)
|
||||
|
||||
static uint32_t Mult(uint8_t x, uint32_t mult) {
|
||||
const uint32_t v = (x * mult + HALF) >> MFIX;
|
||||
assert(v <= 255); // <- 24bit precision is enough to ensure that.
|
||||
return v;
|
||||
}
|
||||
|
||||
#ifdef USE_TABLES_FOR_ALPHA_MULT
|
||||
|
||||
static const uint32_t kMultTables[2][256] = {
|
||||
{ // (255u << MFIX) / alpha
|
||||
0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000,
|
||||
0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2,
|
||||
0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000,
|
||||
0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8,
|
||||
0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3,
|
||||
0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492,
|
||||
0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3,
|
||||
0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8,
|
||||
0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7,
|
||||
0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0,
|
||||
0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4,
|
||||
0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6,
|
||||
0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace,
|
||||
0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a,
|
||||
0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf,
|
||||
0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b,
|
||||
0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d,
|
||||
0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec,
|
||||
0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9,
|
||||
0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249,
|
||||
0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70,
|
||||
0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213,
|
||||
0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10,
|
||||
0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5,
|
||||
0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed,
|
||||
0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a,
|
||||
0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741,
|
||||
0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0,
|
||||
0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e,
|
||||
0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157,
|
||||
0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67,
|
||||
0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4,
|
||||
0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc,
|
||||
0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b,
|
||||
0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830,
|
||||
0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be,
|
||||
0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276,
|
||||
0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc,
|
||||
0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2,
|
||||
0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358,
|
||||
0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0,
|
||||
0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465,
|
||||
0x01030c30, 0x01020612, 0x01010204, 0x01000000 },
|
||||
{ // alpha * KINV_255
|
||||
0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505,
|
||||
0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b,
|
||||
0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111,
|
||||
0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717,
|
||||
0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d,
|
||||
0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323,
|
||||
0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929,
|
||||
0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f,
|
||||
0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535,
|
||||
0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b,
|
||||
0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141,
|
||||
0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747,
|
||||
0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d,
|
||||
0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353,
|
||||
0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959,
|
||||
0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f,
|
||||
0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565,
|
||||
0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b,
|
||||
0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171,
|
||||
0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777,
|
||||
0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d,
|
||||
0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383,
|
||||
0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989,
|
||||
0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f,
|
||||
0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595,
|
||||
0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b,
|
||||
0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1,
|
||||
0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7,
|
||||
0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad,
|
||||
0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3,
|
||||
0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9,
|
||||
0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf,
|
||||
0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5,
|
||||
0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb,
|
||||
0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1,
|
||||
0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7,
|
||||
0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd,
|
||||
0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3,
|
||||
0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9,
|
||||
0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef,
|
||||
0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5,
|
||||
0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb,
|
||||
0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff }
|
||||
};
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) {
|
||||
return kMultTables[!inverse][a];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) {
|
||||
return inverse ? (255u << MFIX) / a : a * KINV_255;
|
||||
}
|
||||
|
||||
#endif // USE_TABLES_FOR_ALPHA_MULT
|
||||
|
||||
void WebPMultARGBRowC(uint32_t* const ptr, int width, int inverse) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t argb = ptr[x];
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
ptr[x] = 0;
|
||||
} else {
|
||||
const uint32_t alpha = (argb >> 24) & 0xff;
|
||||
const uint32_t scale = GetScale(alpha, inverse);
|
||||
uint32_t out = argb & 0xff000000u;
|
||||
out |= Mult(argb >> 0, scale) << 0;
|
||||
out |= Mult(argb >> 8, scale) << 8;
|
||||
out |= Mult(argb >> 16, scale) << 16;
|
||||
ptr[x] = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebPMultRowC(uint8_t* const ptr, const uint8_t* const alpha,
|
||||
int width, int inverse) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t a = alpha[x];
|
||||
if (a != 255) {
|
||||
if (a == 0) {
|
||||
ptr[x] = 0;
|
||||
} else {
|
||||
const uint32_t scale = GetScale(a, inverse);
|
||||
ptr[x] = Mult(ptr[x], scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef KINV_255
|
||||
#undef HALF
|
||||
#undef MFIX
|
||||
|
||||
void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
|
||||
void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
|
||||
int width, int inverse);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic per-plane calls
|
||||
|
||||
void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
|
||||
int inverse) {
|
||||
int n;
|
||||
for (n = 0; n < num_rows; ++n) {
|
||||
WebPMultARGBRow((uint32_t*)ptr, width, inverse);
|
||||
ptr += stride;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPMultRows(uint8_t* ptr, int stride,
|
||||
const uint8_t* alpha, int alpha_stride,
|
||||
int width, int num_rows, int inverse) {
|
||||
int n;
|
||||
for (n = 0; n < num_rows; ++n) {
|
||||
WebPMultRow(ptr, alpha, width, inverse);
|
||||
ptr += stride;
|
||||
alpha += alpha_stride;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Premultiplied modes
|
||||
|
||||
// non dithered-modes
|
||||
|
||||
// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
|
||||
// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
|
||||
// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
|
||||
#if 1 // (int)(x * a / 255.)
|
||||
#define MULTIPLIER(a) ((a) * 32897U)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
|
||||
#else // (int)(x * a / 255. + .5)
|
||||
#define MULTIPLIER(a) ((a) * 65793U)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m) + (1U << 23)) >> 24)
|
||||
#endif
|
||||
|
||||
static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
|
||||
int w, int h, int stride) {
|
||||
while (h-- > 0) {
|
||||
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
|
||||
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t a = alpha[4 * i];
|
||||
if (a != 0xff) {
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
|
||||
rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
|
||||
rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
|
||||
}
|
||||
}
|
||||
rgba += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
#undef PREMULTIPLY
|
||||
|
||||
// rgbA4444
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||
|
||||
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
||||
return (x & 0xf0) | (x >> 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
|
||||
return (x & 0x0f) | (x << 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
|
||||
return (x * m) >> 16;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void ApplyAlphaMultiply4444(uint8_t* rgba4444,
|
||||
int w, int h, int stride,
|
||||
int rg_byte_pos /* 0 or 1 */) {
|
||||
while (h-- > 0) {
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t rg = rgba4444[2 * i + rg_byte_pos];
|
||||
const uint32_t ba = rgba4444[2 * i + (rg_byte_pos ^ 1)];
|
||||
const uint8_t a = ba & 0x0f;
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
const uint8_t r = multiply(dither_hi(rg), mult);
|
||||
const uint8_t g = multiply(dither_lo(rg), mult);
|
||||
const uint8_t b = multiply(dither_hi(ba), mult);
|
||||
rgba4444[2 * i + rg_byte_pos] = (r & 0xf0) | ((g >> 4) & 0x0f);
|
||||
rgba4444[2 * i + (rg_byte_pos ^ 1)] = (b & 0xf0) | a;
|
||||
}
|
||||
rgba4444 += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
|
||||
static void ApplyAlphaMultiply_16b(uint8_t* rgba4444,
|
||||
int w, int h, int stride) {
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
ApplyAlphaMultiply4444(rgba4444, w, h, stride, 1);
|
||||
#else
|
||||
ApplyAlphaMultiply4444(rgba4444, w, h, stride, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int DispatchAlpha_C(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride) {
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
|
||||
return (alpha_mask != 0xff);
|
||||
}
|
||||
|
||||
static void DispatchAlphaToGreen_C(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint32_t* dst, int dst_stride) {
|
||||
int i, j;
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
dst[i] = alpha[i] << 8; // leave A/R/B channels zero'd.
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static int ExtractAlpha_C(const uint8_t* argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* alpha, int alpha_stride) {
|
||||
uint8_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint8_t alpha_value = argb[4 * i];
|
||||
alpha[i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
argb += argb_stride;
|
||||
alpha += alpha_stride;
|
||||
}
|
||||
return (alpha_mask == 0xff);
|
||||
}
|
||||
|
||||
static void ExtractGreen_C(const uint32_t* argb, uint8_t* alpha, int size) {
|
||||
int i;
|
||||
for (i = 0; i < size; ++i) alpha[i] = argb[i] >> 8;
|
||||
}
|
||||
|
||||
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int);
|
||||
void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int);
|
||||
int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
|
||||
void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int);
|
||||
int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
|
||||
void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Init function
|
||||
|
||||
extern void WebPInitAlphaProcessingMIPSdspR2(void);
|
||||
extern void WebPInitAlphaProcessingSSE2(void);
|
||||
extern void WebPInitAlphaProcessingSSE41(void);
|
||||
extern void WebPInitAlphaProcessingNEON(void);
|
||||
|
||||
static volatile VP8CPUInfo alpha_processing_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&alpha_processing_last_cpuinfo_used;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) {
|
||||
if (alpha_processing_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
WebPMultARGBRow = WebPMultARGBRowC;
|
||||
WebPMultRow = WebPMultRowC;
|
||||
WebPApplyAlphaMultiply = ApplyAlphaMultiply;
|
||||
WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b;
|
||||
|
||||
WebPDispatchAlpha = DispatchAlpha_C;
|
||||
WebPDispatchAlphaToGreen = DispatchAlphaToGreen_C;
|
||||
WebPExtractAlpha = ExtractAlpha_C;
|
||||
WebPExtractGreen = ExtractGreen_C;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
WebPInitAlphaProcessingSSE2();
|
||||
#if defined(WEBP_USE_SSE41)
|
||||
if (VP8GetCPUInfo(kSSE4_1)) {
|
||||
WebPInitAlphaProcessingSSE41();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
WebPInitAlphaProcessingNEON();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
WebPInitAlphaProcessingMIPSdspR2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
alpha_processing_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
// 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for processing transparent channel.
|
||||
//
|
||||
// Author(s): Branimir Vasic (branimir.vasic@imgtec.com)
|
||||
// Djordje Pesut (djordje.pesut@imgtec.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
|
||||
static int DispatchAlpha(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride) {
|
||||
uint32_t alpha_mask = 0xffffffff;
|
||||
int i, j, temp0;
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
uint8_t* pdst = dst;
|
||||
const uint8_t* palpha = alpha;
|
||||
for (i = 0; i < (width >> 2); ++i) {
|
||||
int temp1, temp2, temp3;
|
||||
|
||||
__asm__ volatile (
|
||||
"ulw %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 4 \n\t"
|
||||
"addiu %[pdst], %[pdst], 16 \n\t"
|
||||
"srl %[temp1], %[temp0], 8 \n\t"
|
||||
"srl %[temp2], %[temp0], 16 \n\t"
|
||||
"srl %[temp3], %[temp0], 24 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"sb %[temp0], -16(%[pdst]) \n\t"
|
||||
"sb %[temp1], -12(%[pdst]) \n\t"
|
||||
"sb %[temp2], -8(%[pdst]) \n\t"
|
||||
"sb %[temp3], -4(%[pdst]) \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[temp3]"=&r"(temp3), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
|
||||
[alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0; i < (width & 3); ++i) {
|
||||
__asm__ volatile (
|
||||
"lbu %[temp0], 0(%[palpha]) \n\t"
|
||||
"addiu %[palpha], %[palpha], 1 \n\t"
|
||||
"sb %[temp0], 0(%[pdst]) \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"addiu %[pdst], %[pdst], 4 \n\t"
|
||||
: [temp0]"=&r"(temp0), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
|
||||
[alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
|
||||
__asm__ volatile (
|
||||
"ext %[temp0], %[alpha_mask], 0, 16 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 16 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
"ext %[temp0], %[alpha_mask], 0, 8 \n\t"
|
||||
"srl %[alpha_mask], %[alpha_mask], 8 \n\t"
|
||||
"and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
|
||||
: [temp0]"=&r"(temp0), [alpha_mask]"+r"(alpha_mask)
|
||||
:
|
||||
);
|
||||
|
||||
return (alpha_mask != 0xff);
|
||||
}
|
||||
|
||||
static void MultARGBRow(uint32_t* const ptr, int width, int inverse) {
|
||||
int x;
|
||||
const uint32_t c_00ffffff = 0x00ffffffu;
|
||||
const uint32_t c_ff000000 = 0xff000000u;
|
||||
const uint32_t c_8000000 = 0x00800000u;
|
||||
const uint32_t c_8000080 = 0x00800080u;
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t argb = ptr[x];
|
||||
if (argb < 0xff000000u) { // alpha < 255
|
||||
if (argb <= 0x00ffffffu) { // alpha == 0
|
||||
ptr[x] = 0;
|
||||
} else {
|
||||
int temp0, temp1, temp2, temp3, alpha;
|
||||
__asm__ volatile (
|
||||
"srl %[alpha], %[argb], 24 \n\t"
|
||||
"replv.qb %[temp0], %[alpha] \n\t"
|
||||
"and %[temp0], %[temp0], %[c_00ffffff] \n\t"
|
||||
"beqz %[inverse], 0f \n\t"
|
||||
"divu $zero, %[c_ff000000], %[alpha] \n\t"
|
||||
"mflo %[temp0] \n\t"
|
||||
"0: \n\t"
|
||||
"andi %[temp1], %[argb], 0xff \n\t"
|
||||
"ext %[temp2], %[argb], 8, 8 \n\t"
|
||||
"ext %[temp3], %[argb], 16, 8 \n\t"
|
||||
"mul %[temp1], %[temp1], %[temp0] \n\t"
|
||||
"mul %[temp2], %[temp2], %[temp0] \n\t"
|
||||
"mul %[temp3], %[temp3], %[temp0] \n\t"
|
||||
"precrq.ph.w %[temp1], %[temp2], %[temp1] \n\t"
|
||||
"addu %[temp3], %[temp3], %[c_8000000] \n\t"
|
||||
"addu %[temp1], %[temp1], %[c_8000080] \n\t"
|
||||
"precrq.ph.w %[temp3], %[argb], %[temp3] \n\t"
|
||||
"precrq.qb.ph %[temp1], %[temp3], %[temp1] \n\t"
|
||||
: [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
|
||||
[temp3]"=&r"(temp3), [alpha]"=&r"(alpha)
|
||||
: [inverse]"r"(inverse), [c_00ffffff]"r"(c_00ffffff),
|
||||
[c_8000000]"r"(c_8000000), [c_8000080]"r"(c_8000080),
|
||||
[c_ff000000]"r"(c_ff000000), [argb]"r"(argb)
|
||||
: "memory", "hi", "lo"
|
||||
);
|
||||
ptr[x] = temp1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
||||
extern void WebPInitAlphaProcessingMIPSdspR2(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingMIPSdspR2(void) {
|
||||
WebPDispatchAlpha = DispatchAlpha;
|
||||
WebPMultARGBRow = MultARGBRow;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_MIPS_DSP_R2
|
||||
|
||||
WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingMIPSdspR2)
|
||||
|
||||
#endif // WEBP_USE_MIPS_DSP_R2
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
// Copyright 2017 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 processing transparent channel, NEON version.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
|
||||
#include "./neon.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x8081)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
|
||||
|
||||
#define MULTIPLY_BY_ALPHA(V, ALPHA, OTHER) do { \
|
||||
const uint8x8_t alpha = (V).val[(ALPHA)]; \
|
||||
const uint16x8_t r1 = vmull_u8((V).val[1], alpha); \
|
||||
const uint16x8_t g1 = vmull_u8((V).val[2], alpha); \
|
||||
const uint16x8_t b1 = vmull_u8((V).val[(OTHER)], alpha); \
|
||||
/* we use: v / 255 = (v + 1 + (v >> 8)) >> 8 */ \
|
||||
const uint16x8_t r2 = vsraq_n_u16(r1, r1, 8); \
|
||||
const uint16x8_t g2 = vsraq_n_u16(g1, g1, 8); \
|
||||
const uint16x8_t b2 = vsraq_n_u16(b1, b1, 8); \
|
||||
const uint16x8_t r3 = vaddq_u16(r2, kOne); \
|
||||
const uint16x8_t g3 = vaddq_u16(g2, kOne); \
|
||||
const uint16x8_t b3 = vaddq_u16(b2, kOne); \
|
||||
(V).val[1] = vshrn_n_u16(r3, 8); \
|
||||
(V).val[2] = vshrn_n_u16(g3, 8); \
|
||||
(V).val[(OTHER)] = vshrn_n_u16(b3, 8); \
|
||||
} while (0)
|
||||
|
||||
static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first,
|
||||
int w, int h, int stride) {
|
||||
const uint16x8_t kOne = vdupq_n_u16(1u);
|
||||
while (h-- > 0) {
|
||||
uint32_t* const rgbx = (uint32_t*)rgba;
|
||||
int i = 0;
|
||||
if (alpha_first) {
|
||||
for (; i + 8 <= w; i += 8) {
|
||||
// load aaaa...|rrrr...|gggg...|bbbb...
|
||||
uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i));
|
||||
MULTIPLY_BY_ALPHA(RGBX, 0, 3);
|
||||
vst4_u8((uint8_t*)(rgbx + i), RGBX);
|
||||
}
|
||||
} else {
|
||||
for (; i + 8 <= w; i += 8) {
|
||||
uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i));
|
||||
MULTIPLY_BY_ALPHA(RGBX, 3, 0);
|
||||
vst4_u8((uint8_t*)(rgbx + i), RGBX);
|
||||
}
|
||||
}
|
||||
// Finish with left-overs.
|
||||
for (; i < w; ++i) {
|
||||
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
|
||||
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
|
||||
const uint32_t a = alpha[4 * i];
|
||||
if (a != 0xff) {
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
|
||||
rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
|
||||
rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
|
||||
}
|
||||
}
|
||||
rgba += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLY_BY_ALPHA
|
||||
#undef MULTIPLIER
|
||||
#undef PREMULTIPLY
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride) {
|
||||
uint32_t alpha_mask = 0xffffffffu;
|
||||
uint8x8_t mask8 = vdup_n_u8(0xff);
|
||||
uint32_t tmp[2];
|
||||
int i, j;
|
||||
for (j = 0; j < height; ++j) {
|
||||
// We don't know if alpha is first or last in dst[] (depending on rgbA/Argb
|
||||
// mode). So we must be sure dst[4*i + 8 - 1] is writable for the store.
|
||||
// Hence the test with 'width - 1' instead of just 'width'.
|
||||
for (i = 0; i + 8 <= width - 1; i += 8) {
|
||||
uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(dst + 4 * i));
|
||||
const uint8x8_t alphas = vld1_u8(alpha + i);
|
||||
rgbX.val[0] = alphas;
|
||||
vst4_u8((uint8_t*)(dst + 4 * i), rgbX);
|
||||
mask8 = vand_u8(mask8, alphas);
|
||||
}
|
||||
for (; i < width; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
vst1_u8((uint8_t*)tmp, mask8);
|
||||
alpha_mask &= tmp[0];
|
||||
alpha_mask &= tmp[1];
|
||||
return (alpha_mask != 0xffffffffu);
|
||||
}
|
||||
|
||||
static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint32_t* dst, int dst_stride) {
|
||||
int i, j;
|
||||
uint8x8x4_t greens; // leave A/R/B channels zero'd.
|
||||
greens.val[0] = vdup_n_u8(0);
|
||||
greens.val[2] = vdup_n_u8(0);
|
||||
greens.val[3] = vdup_n_u8(0);
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i + 8 <= width; i += 8) {
|
||||
greens.val[1] = vld1_u8(alpha + i);
|
||||
vst4_u8((uint8_t*)(dst + i), greens);
|
||||
}
|
||||
for (; i < width; ++i) dst[i] = alpha[i] << 8;
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* alpha, int alpha_stride) {
|
||||
uint32_t alpha_mask = 0xffffffffu;
|
||||
uint8x8_t mask8 = vdup_n_u8(0xff);
|
||||
uint32_t tmp[2];
|
||||
int i, j;
|
||||
for (j = 0; j < height; ++j) {
|
||||
// We don't know if alpha is first or last in dst[] (depending on rgbA/Argb
|
||||
// mode). So we must be sure dst[4*i + 8 - 1] is writable for the store.
|
||||
// Hence the test with 'width - 1' instead of just 'width'.
|
||||
for (i = 0; i + 8 <= width - 1; i += 8) {
|
||||
const uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(argb + 4 * i));
|
||||
const uint8x8_t alphas = rgbX.val[0];
|
||||
vst1_u8((uint8_t*)(alpha + i), alphas);
|
||||
mask8 = vand_u8(mask8, alphas);
|
||||
}
|
||||
for (; i < width; ++i) {
|
||||
alpha[i] = argb[4 * i];
|
||||
alpha_mask &= alpha[i];
|
||||
}
|
||||
argb += argb_stride;
|
||||
alpha += alpha_stride;
|
||||
}
|
||||
vst1_u8((uint8_t*)tmp, mask8);
|
||||
alpha_mask &= tmp[0];
|
||||
alpha_mask &= tmp[1];
|
||||
return (alpha_mask == 0xffffffffu);
|
||||
}
|
||||
|
||||
static void ExtractGreen_NEON(const uint32_t* argb,
|
||||
uint8_t* alpha, int size) {
|
||||
int i;
|
||||
for (i = 0; i + 16 <= size; i += 16) {
|
||||
const uint8x16x4_t rgbX = vld4q_u8((const uint8_t*)(argb + i));
|
||||
const uint8x16_t greens = rgbX.val[1];
|
||||
vst1q_u8(alpha + i, greens);
|
||||
}
|
||||
for (; i < size; ++i) alpha[i] = (argb[i] >> 8) & 0xff;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
extern void WebPInitAlphaProcessingNEON(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingNEON(void) {
|
||||
WebPApplyAlphaMultiply = ApplyAlphaMultiply_NEON;
|
||||
WebPDispatchAlpha = DispatchAlpha_NEON;
|
||||
WebPDispatchAlphaToGreen = DispatchAlphaToGreen_NEON;
|
||||
WebPExtractAlpha = ExtractAlpha_NEON;
|
||||
WebPExtractGreen = ExtractGreen_NEON;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_NEON
|
||||
|
||||
WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingNEON)
|
||||
|
||||
#endif // WEBP_USE_NEON
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for processing transparent channel.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int DispatchAlpha(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride) {
|
||||
// alpha_and stores an 'and' operation of all the alpha[] values. The final
|
||||
// value is not 0xff if any of the alpha[] is not equal to 0xff.
|
||||
uint32_t alpha_and = 0xff;
|
||||
int i, j;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i rgb_mask = _mm_set1_epi32(0xffffff00u); // to preserve RGB
|
||||
const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
|
||||
__m128i all_alphas = all_0xff;
|
||||
|
||||
// We must be able to access 3 extra bytes after the last written byte
|
||||
// 'dst[4 * width - 4]', because we don't know if alpha is the first or the
|
||||
// last byte of the quadruplet.
|
||||
const int limit = (width - 1) & ~7;
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
__m128i* out = (__m128i*)dst;
|
||||
for (i = 0; i < limit; i += 8) {
|
||||
// load 8 alpha bytes
|
||||
const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[i]);
|
||||
const __m128i a1 = _mm_unpacklo_epi8(a0, zero);
|
||||
const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);
|
||||
const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);
|
||||
// load 8 dst pixels (32 bytes)
|
||||
const __m128i b0_lo = _mm_loadu_si128(out + 0);
|
||||
const __m128i b0_hi = _mm_loadu_si128(out + 1);
|
||||
// mask dst alpha values
|
||||
const __m128i b1_lo = _mm_and_si128(b0_lo, rgb_mask);
|
||||
const __m128i b1_hi = _mm_and_si128(b0_hi, rgb_mask);
|
||||
// combine
|
||||
const __m128i b2_lo = _mm_or_si128(b1_lo, a2_lo);
|
||||
const __m128i b2_hi = _mm_or_si128(b1_hi, a2_hi);
|
||||
// store
|
||||
_mm_storeu_si128(out + 0, b2_lo);
|
||||
_mm_storeu_si128(out + 1, b2_hi);
|
||||
// accumulate eight alpha 'and' in parallel
|
||||
all_alphas = _mm_and_si128(all_alphas, a0);
|
||||
out += 2;
|
||||
}
|
||||
for (; i < width; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_and &= alpha_value;
|
||||
}
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
// Combine the eight alpha 'and' into a 8-bit mask.
|
||||
alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
|
||||
return (alpha_and != 0xff);
|
||||
}
|
||||
|
||||
static void DispatchAlphaToGreen(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint32_t* dst, int dst_stride) {
|
||||
int i, j;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const int limit = width & ~15;
|
||||
for (j = 0; j < height; ++j) {
|
||||
for (i = 0; i < limit; i += 16) { // process 16 alpha bytes
|
||||
const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]);
|
||||
const __m128i a1 = _mm_unpacklo_epi8(zero, a0); // note the 'zero' first!
|
||||
const __m128i b1 = _mm_unpackhi_epi8(zero, a0);
|
||||
const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);
|
||||
const __m128i b2_lo = _mm_unpacklo_epi16(b1, zero);
|
||||
const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);
|
||||
const __m128i b2_hi = _mm_unpackhi_epi16(b1, zero);
|
||||
_mm_storeu_si128((__m128i*)&dst[i + 0], a2_lo);
|
||||
_mm_storeu_si128((__m128i*)&dst[i + 4], a2_hi);
|
||||
_mm_storeu_si128((__m128i*)&dst[i + 8], b2_lo);
|
||||
_mm_storeu_si128((__m128i*)&dst[i + 12], b2_hi);
|
||||
}
|
||||
for (; i < width; ++i) dst[i] = alpha[i] << 8;
|
||||
alpha += alpha_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static int ExtractAlpha(const uint8_t* argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* alpha, int alpha_stride) {
|
||||
// alpha_and stores an 'and' operation of all the alpha[] values. The final
|
||||
// value is not 0xff if any of the alpha[] is not equal to 0xff.
|
||||
uint32_t alpha_and = 0xff;
|
||||
int i, j;
|
||||
const __m128i a_mask = _mm_set1_epi32(0xffu); // to preserve alpha
|
||||
const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
|
||||
__m128i all_alphas = all_0xff;
|
||||
|
||||
// We must be able to access 3 extra bytes after the last written byte
|
||||
// 'src[4 * width - 4]', because we don't know if alpha is the first or the
|
||||
// last byte of the quadruplet.
|
||||
const int limit = (width - 1) & ~7;
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
const __m128i* src = (const __m128i*)argb;
|
||||
for (i = 0; i < limit; i += 8) {
|
||||
// load 32 argb bytes
|
||||
const __m128i a0 = _mm_loadu_si128(src + 0);
|
||||
const __m128i a1 = _mm_loadu_si128(src + 1);
|
||||
const __m128i b0 = _mm_and_si128(a0, a_mask);
|
||||
const __m128i b1 = _mm_and_si128(a1, a_mask);
|
||||
const __m128i c0 = _mm_packs_epi32(b0, b1);
|
||||
const __m128i d0 = _mm_packus_epi16(c0, c0);
|
||||
// store
|
||||
_mm_storel_epi64((__m128i*)&alpha[i], d0);
|
||||
// accumulate eight alpha 'and' in parallel
|
||||
all_alphas = _mm_and_si128(all_alphas, d0);
|
||||
src += 2;
|
||||
}
|
||||
for (; i < width; ++i) {
|
||||
const uint32_t alpha_value = argb[4 * i];
|
||||
alpha[i] = alpha_value;
|
||||
alpha_and &= alpha_value;
|
||||
}
|
||||
argb += argb_stride;
|
||||
alpha += alpha_stride;
|
||||
}
|
||||
// Combine the eight alpha 'and' into a 8-bit mask.
|
||||
alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
|
||||
return (alpha_and == 0xff);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Non-dither premultiplied modes
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x8081)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
|
||||
|
||||
// We can't use a 'const int' for the SHUFFLE value, because it has to be an
|
||||
// immediate in the _mm_shufflexx_epi16() instruction. We really need a macro.
|
||||
// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit
|
||||
// value.
|
||||
#define APPLY_ALPHA(RGBX, SHUFFLE) do { \
|
||||
const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \
|
||||
const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \
|
||||
const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \
|
||||
const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \
|
||||
const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \
|
||||
const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \
|
||||
const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \
|
||||
const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \
|
||||
const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \
|
||||
/* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \
|
||||
const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \
|
||||
const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \
|
||||
const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \
|
||||
const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \
|
||||
const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \
|
||||
const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \
|
||||
const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \
|
||||
_mm_storeu_si128((__m128i*)&(RGBX), A3); \
|
||||
} while (0)
|
||||
|
||||
static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
|
||||
int w, int h, int stride) {
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i kMult = _mm_set1_epi16(0x8081u);
|
||||
const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);
|
||||
const int kSpan = 4;
|
||||
while (h-- > 0) {
|
||||
uint32_t* const rgbx = (uint32_t*)rgba;
|
||||
int i;
|
||||
if (!alpha_first) {
|
||||
for (i = 0; i + kSpan <= w; i += kSpan) {
|
||||
APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i + kSpan <= w; i += kSpan) {
|
||||
APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
// Finish with left-overs.
|
||||
for (; i < w; ++i) {
|
||||
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
|
||||
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
|
||||
const uint32_t a = alpha[4 * i];
|
||||
if (a != 0xff) {
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
|
||||
rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
|
||||
rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
|
||||
}
|
||||
}
|
||||
rgba += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
#undef PREMULTIPLY
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Apply alpha value to rows
|
||||
|
||||
static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) {
|
||||
int x = 0;
|
||||
if (!inverse) {
|
||||
const int kSpan = 2;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i k128 = _mm_set1_epi16(128);
|
||||
const __m128i kMult = _mm_set1_epi16(0x0101);
|
||||
const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0);
|
||||
for (x = 0; x + kSpan <= width; x += kSpan) {
|
||||
// To compute 'result = (int)(a * x / 255. + .5)', we use:
|
||||
// tmp = a * v + 128, result = (tmp * 0x0101u) >> 16
|
||||
const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]);
|
||||
const __m128i A1 = _mm_unpacklo_epi8(A0, zero);
|
||||
const __m128i A2 = _mm_or_si128(A1, kMask);
|
||||
const __m128i A3 = _mm_shufflelo_epi16(A2, _MM_SHUFFLE(2, 3, 3, 3));
|
||||
const __m128i A4 = _mm_shufflehi_epi16(A3, _MM_SHUFFLE(2, 3, 3, 3));
|
||||
// here, A4 = [ff a0 a0 a0][ff a1 a1 a1]
|
||||
const __m128i A5 = _mm_mullo_epi16(A4, A1);
|
||||
const __m128i A6 = _mm_add_epi16(A5, k128);
|
||||
const __m128i A7 = _mm_mulhi_epu16(A6, kMult);
|
||||
const __m128i A10 = _mm_packus_epi16(A7, zero);
|
||||
_mm_storel_epi64((__m128i*)&ptr[x], A10);
|
||||
}
|
||||
}
|
||||
width -= x;
|
||||
if (width > 0) WebPMultARGBRowC(ptr + x, width, inverse);
|
||||
}
|
||||
|
||||
static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha,
|
||||
int width, int inverse) {
|
||||
int x = 0;
|
||||
if (!inverse) {
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i k128 = _mm_set1_epi16(128);
|
||||
const __m128i kMult = _mm_set1_epi16(0x0101);
|
||||
for (x = 0; x + 8 <= width; x += 8) {
|
||||
const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]);
|
||||
const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]);
|
||||
const __m128i v1 = _mm_unpacklo_epi8(v0, zero);
|
||||
const __m128i a1 = _mm_unpacklo_epi8(a0, zero);
|
||||
const __m128i v2 = _mm_mullo_epi16(v1, a1);
|
||||
const __m128i v3 = _mm_add_epi16(v2, k128);
|
||||
const __m128i v4 = _mm_mulhi_epu16(v3, kMult);
|
||||
const __m128i v5 = _mm_packus_epi16(v4, zero);
|
||||
_mm_storel_epi64((__m128i*)&ptr[x], v5);
|
||||
}
|
||||
}
|
||||
width -= x;
|
||||
if (width > 0) WebPMultRowC(ptr + x, alpha + x, width, inverse);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
||||
extern void WebPInitAlphaProcessingSSE2(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {
|
||||
WebPMultARGBRow = MultARGBRow_SSE2;
|
||||
WebPMultRow = MultRow_SSE2;
|
||||
WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2;
|
||||
WebPDispatchAlpha = DispatchAlpha;
|
||||
WebPDispatchAlphaToGreen = DispatchAlphaToGreen;
|
||||
WebPExtractAlpha = ExtractAlpha;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_SSE2
|
||||
|
||||
WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE2)
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// 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 processing transparent channel, SSE4.1 variant.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_SSE41)
|
||||
|
||||
#include <smmintrin.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int ExtractAlpha(const uint8_t* argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* alpha, int alpha_stride) {
|
||||
// alpha_and stores an 'and' operation of all the alpha[] values. The final
|
||||
// value is not 0xff if any of the alpha[] is not equal to 0xff.
|
||||
uint32_t alpha_and = 0xff;
|
||||
int i, j;
|
||||
const __m128i all_0xff = _mm_set1_epi32(~0u);
|
||||
__m128i all_alphas = all_0xff;
|
||||
|
||||
// We must be able to access 3 extra bytes after the last written byte
|
||||
// 'src[4 * width - 4]', because we don't know if alpha is the first or the
|
||||
// last byte of the quadruplet.
|
||||
const int limit = (width - 1) & ~15;
|
||||
const __m128i kCstAlpha0 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 12, 8, 4, 0);
|
||||
const __m128i kCstAlpha1 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
12, 8, 4, 0, -1, -1, -1, -1);
|
||||
const __m128i kCstAlpha2 = _mm_set_epi8(-1, -1, -1, -1, 12, 8, 4, 0,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1);
|
||||
const __m128i kCstAlpha3 = _mm_set_epi8(12, 8, 4, 0, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1);
|
||||
for (j = 0; j < height; ++j) {
|
||||
const __m128i* src = (const __m128i*)argb;
|
||||
for (i = 0; i < limit; i += 16) {
|
||||
// load 64 argb bytes
|
||||
const __m128i a0 = _mm_loadu_si128(src + 0);
|
||||
const __m128i a1 = _mm_loadu_si128(src + 1);
|
||||
const __m128i a2 = _mm_loadu_si128(src + 2);
|
||||
const __m128i a3 = _mm_loadu_si128(src + 3);
|
||||
const __m128i b0 = _mm_shuffle_epi8(a0, kCstAlpha0);
|
||||
const __m128i b1 = _mm_shuffle_epi8(a1, kCstAlpha1);
|
||||
const __m128i b2 = _mm_shuffle_epi8(a2, kCstAlpha2);
|
||||
const __m128i b3 = _mm_shuffle_epi8(a3, kCstAlpha3);
|
||||
const __m128i c0 = _mm_or_si128(b0, b1);
|
||||
const __m128i c1 = _mm_or_si128(b2, b3);
|
||||
const __m128i d0 = _mm_or_si128(c0, c1);
|
||||
// store
|
||||
_mm_storeu_si128((__m128i*)&alpha[i], d0);
|
||||
// accumulate sixteen alpha 'and' in parallel
|
||||
all_alphas = _mm_and_si128(all_alphas, d0);
|
||||
src += 4;
|
||||
}
|
||||
for (; i < width; ++i) {
|
||||
const uint32_t alpha_value = argb[4 * i];
|
||||
alpha[i] = alpha_value;
|
||||
alpha_and &= alpha_value;
|
||||
}
|
||||
argb += argb_stride;
|
||||
alpha += alpha_stride;
|
||||
}
|
||||
// Combine the sixteen alpha 'and' into an 8-bit mask.
|
||||
alpha_and |= 0xff00u; // pretend the upper bits [8..15] were tested ok.
|
||||
alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
|
||||
return (alpha_and == 0xffffu);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
||||
extern void WebPInitAlphaProcessingSSE41(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE41(void) {
|
||||
WebPExtractAlpha = ExtractAlpha;
|
||||
}
|
||||
|
||||
#else // !WEBP_USE_SSE41
|
||||
|
||||
WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE41)
|
||||
|
||||
#endif // WEBP_USE_SSE41
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
// 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.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// ARGB making functions.
|
||||
//
|
||||
// Author: Djordje Pesut (djordje.pesut@imgtec.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
|
||||
return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b);
|
||||
}
|
||||
|
||||
static void PackARGB(const uint8_t* a, const uint8_t* r, const uint8_t* g,
|
||||
const uint8_t* b, int len, uint32_t* out) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void PackRGB(const uint8_t* r, const uint8_t* g, const uint8_t* b,
|
||||
int len, int step, uint32_t* out) {
|
||||
int i, offset = 0;
|
||||
for (i = 0; i < len; ++i) {
|
||||
out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]);
|
||||
offset += step;
|
||||
}
|
||||
}
|
||||
|
||||
void (*VP8PackARGB)(const uint8_t*, const uint8_t*, const uint8_t*,
|
||||
const uint8_t*, int, uint32_t*);
|
||||
void (*VP8PackRGB)(const uint8_t*, const uint8_t*, const uint8_t*,
|
||||
int, int, uint32_t*);
|
||||
|
||||
extern void VP8EncDspARGBInitMIPSdspR2(void);
|
||||
extern void VP8EncDspARGBInitSSE2(void);
|
||||
|
||||
static volatile VP8CPUInfo argb_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&argb_last_cpuinfo_used;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspARGBInit(void) {
|
||||
if (argb_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
VP8PackARGB = PackARGB;
|
||||
VP8PackRGB = PackRGB;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
VP8EncDspARGBInitSSE2();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
VP8EncDspARGBInitMIPSdspR2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
argb_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user