gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 1a3f26fb5c515f5672561449f91f6be6383b5a3a
parent f9e5ec99bd29018088232417e634fc1731d1cb08
Author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
Date:   Tue,  6 Dec 2022 14:15:25 +0100

[feature] media: add webp support (#1155)

* media: add webp support

Signed-off-by: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>

* bump exif-terminator to v0.5.0

Signed-off-by: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>

Signed-off-by: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
Diffstat:
Mgo.mod | 4++--
Mgo.sum | 14++++++++++----
Minternal/api/client/instance/instancepatch_test.go | 10+++++-----
Minternal/media/image.go | 7++++---
Minternal/media/processingmedia.go | 4++--
Minternal/media/types.go | 3+++
Minternal/media/util.go | 2++
Mvendor/github.com/superseriousbusiness/exif-terminator/jpeg.go | 2+-
Mvendor/github.com/superseriousbusiness/exif-terminator/terminator.go | 14++++++++++++++
Avendor/github.com/superseriousbusiness/exif-terminator/webp.go | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/riff/riff.go | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/decode.go | 403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/filter.go | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/idct.go | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/partition.go | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/pred.go | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/predfunc.go | 553+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/quant.go | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/reconstruct.go | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8/token.go | 381+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8l/decode.go | 603+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8l/huffman.go | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/vp8l/transform.go | 299+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/webp/decode.go | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/golang.org/x/image/webp/doc.go | 9+++++++++
Mvendor/modules.txt | 8++++++--
26 files changed, 4348 insertions(+), 19 deletions(-)

diff --git a/go.mod b/go.mod @@ -40,7 +40,7 @@ require ( github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 github.com/superseriousbusiness/activity v1.2.1-gts - github.com/superseriousbusiness/exif-terminator v0.4.0 + github.com/superseriousbusiness/exif-terminator v0.5.0 github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB github.com/tdewolff/minify/v2 v2.12.4 github.com/ulule/limiter/v3 v3.10.0 @@ -50,6 +50,7 @@ require ( github.com/wagslane/go-password-validator v0.3.0 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d + golang.org/x/image v0.1.0 golang.org/x/net v0.0.0-20221014081412-f15817d10f9b golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 golang.org/x/text v0.4.0 @@ -134,7 +135,6 @@ require ( github.com/ugorji/go/codec v1.2.7 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/tools v0.1.12 // indirect diff --git a/go.sum b/go.sum @@ -571,8 +571,8 @@ github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/superseriousbusiness/activity v1.2.1-gts h1:wh7v0zYa1mJmqB35PSfvgl4cs51Dh5PyfKvcZLSxMQU= github.com/superseriousbusiness/activity v1.2.1-gts/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM= -github.com/superseriousbusiness/exif-terminator v0.4.0 h1:pzAg7luCi8oc2LVDwgTLvTinh/+/2UuWgJZrM8MMaT4= -github.com/superseriousbusiness/exif-terminator v0.4.0/go.mod h1:OPfOSEDWjXaW3BILJBN89j0VLD8bglmHwHHwwwSLb5A= +github.com/superseriousbusiness/exif-terminator v0.5.0 h1:57SO/geyaOl2v/lJSQLVcQbdghpyFuK8ZTtaHL81fUQ= +github.com/superseriousbusiness/exif-terminator v0.5.0/go.mod h1:d5IkskXco/3XRXzOrI73uGYn+wahJEqPlQSSqn6jxSw= github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe h1:ksl2oCx/Qo8sNDc3Grb8WGKBM9nkvhCm25uvlT86azE= github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4= github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB h1:PtW2w6budTvRV2J5QAoSvThTHBuvh8t/+BXIZFAaBSc= @@ -644,6 +644,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.1/go.mod h1:TSQ0KjMH+pht+bRyvVooJ1rBpvvngSGaPISafq9MxJk= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= @@ -678,6 +679,7 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -698,8 +700,8 @@ golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2F golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY= -golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= +golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= +golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -723,6 +725,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 h1:CZP0Rbk/s1EIiUMx5DS2MhK2ct52xpQxqddVD0FmF+o= golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -774,6 +777,7 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -812,6 +816,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -888,6 +893,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go @@ -65,7 +65,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() { b, err := io.ReadAll(result.Body) suite.NoError(err) - suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"Example Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"someone@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) + suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"Example Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"someone@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png","image/webp"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) } func (suite *InstancePatchTestSuite) TestInstancePatch2() { @@ -95,7 +95,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() { b, err := io.ReadAll(result.Body) suite.NoError(err) - suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"Geoff's Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) + suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"Geoff's Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png","image/webp"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) } func (suite *InstancePatchTestSuite) TestInstancePatch3() { @@ -125,7 +125,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() { b, err := io.ReadAll(result.Body) suite.NoError(err) - suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) + suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png","image/webp"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) } func (suite *InstancePatchTestSuite) TestInstancePatch4() { @@ -216,7 +216,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() { b, err := io.ReadAll(result.Body) suite.NoError(err) - suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) + suite.Equal(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png","image/webp"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/assets/logo.png","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, string(b)) } func (suite *InstancePatchTestSuite) TestInstancePatch7() { @@ -279,7 +279,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { } suite.NotEmpty(instanceAccount.AvatarMediaAttachmentID) - expectedInstanceResponse := fmt.Sprintf(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/fileserver/%s/attachment/original/%s.gif","thumbnail_type":"image/gif","thumbnail_description":"A bouncing little green peglin.","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, instanceAccount.ID, instanceAccount.AvatarMediaAttachmentID) + expectedInstanceResponse := fmt.Sprintf(`{"uri":"http://localhost:8080","account_domain":"localhost:8080","title":"GoToSocial Testrig Instance","description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","short_description":"\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e","email":"admin@example.org","version":"0.0.0-testrig","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":25},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png","image/webp"],"image_size_limit":10485760,"image_matrix_limit":16777216,"video_size_limit":41943040,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746},"accounts":{"allow_custom_css":true},"emojis":{"emoji_size_limit":51200}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"http://localhost:8080/fileserver/%s/attachment/original/%s.gif","thumbnail_type":"image/gif","thumbnail_description":"A bouncing little green peglin.","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"max_toot_chars":5000}`, instanceAccount.ID, instanceAccount.AvatarMediaAttachmentID) suite.Equal(expectedInstanceResponse, string(b)) } diff --git a/internal/media/image.go b/internal/media/image.go @@ -30,6 +30,7 @@ import ( "github.com/buckket/go-blurhash" "github.com/disintegration/imaging" + _ "golang.org/x/image/webp" // blank import to support WebP decoding ) const ( @@ -71,7 +72,7 @@ func decodeImage(r io.Reader, contentType string) (*imageMeta, error) { var err error switch contentType { - case mimeImageJpeg: + case mimeImageJpeg, mimeImageWebp: i, err = imaging.Decode(r, imaging.AutoOrientation(true)) case mimeImagePng: strippedPngReader := io.Reader(&PNGAncillaryChunkStripper{ @@ -104,7 +105,7 @@ func decodeImage(r io.Reader, contentType string) (*imageMeta, error) { } // deriveThumbnail returns a byte slice and metadata for a thumbnail -// of a given jpeg, png, or gif, or an error if something goes wrong. +// of a given jpeg, png, gif or webp, or an error if something goes wrong. // // If createBlurhash is true, then a blurhash will also be generated from a tiny // version of the image. This costs precious CPU cycles, so only use it if you @@ -117,7 +118,7 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima var err error switch contentType { - case mimeImageJpeg, mimeImageGif: + case mimeImageJpeg, mimeImageGif, mimeImageWebp: i, err = imaging.Decode(r, imaging.AutoOrientation(true)) case mimeImagePng: strippedPngReader := io.Reader(&PNGAncillaryChunkStripper{ diff --git a/internal/media/processingmedia.go b/internal/media/processingmedia.go @@ -215,7 +215,7 @@ func (p *ProcessingMedia) loadFullSize(ctx context.Context) error { // decode the image ct := p.attachment.File.ContentType switch ct { - case mimeImageJpeg, mimeImagePng: + case mimeImageJpeg, mimeImagePng, mimeImageWebp: decoded, err = decodeImage(stored, ct) case mimeImageGif: decoded, err = decodeGif(stored) @@ -319,7 +319,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error { p.attachment.Type = gtsmodel.FileTypeImage // nothing to terminate, we can just store the multireader readerToStore = multiReader - case mimeJpeg, mimePng: + case mimeJpeg, mimePng, mimeWebp: p.attachment.Type = gtsmodel.FileTypeImage if fileSize > 0 { terminated, err := terminator.Terminate(multiReader, int(fileSize), extension) diff --git a/internal/media/types.go b/internal/media/types.go @@ -43,6 +43,9 @@ const ( mimePng = "png" mimeImagePng = mimeImage + "/" + mimePng + + mimeWebp = "webp" + mimeImageWebp = mimeImage + "/" + mimeWebp ) type processState int32 diff --git a/internal/media/util.go b/internal/media/util.go @@ -37,6 +37,7 @@ func AllSupportedMIMETypes() []string { mimeImageJpeg, mimeImageGif, mimeImagePng, + mimeImageWebp, } } @@ -68,6 +69,7 @@ func supportedImage(mimeType string) bool { mimeImageJpeg, mimeImageGif, mimeImagePng, + mimeImageWebp, } for _, accepted := range acceptedImageTypes { if mimeType == accepted { diff --git a/vendor/github.com/superseriousbusiness/exif-terminator/jpeg.go b/vendor/github.com/superseriousbusiness/exif-terminator/jpeg.go @@ -91,7 +91,7 @@ func (v *jpegVisitor) HandleSegment(segmentMarker byte, _ string, _ int, _ bool) if segmentMarker == jpegstructure.MARKER_EOI { // take account of the last 2 bytes taken up by the EOI eoiLength := 2 - + // this is the total file size we will // have written including the EOI willHaveWritten := v.writtenTotalBytes + eoiLength diff --git a/vendor/github.com/superseriousbusiness/exif-terminator/terminator.go b/vendor/github.com/superseriousbusiness/exif-terminator/terminator.go @@ -43,6 +43,8 @@ func Terminate(in io.Reader, fileSize int, mediaType string) (io.Reader, error) switch mediaType { case "image/jpeg", "jpeg", "jpg": err = terminateJpeg(scanner, pipeWriter, fileSize) + case "image/webp", "webp": + err = terminateWebp(scanner, pipeWriter) case "image/png", "png": // for pngs we need to skip the header bytes, so read them in // and check we're really dealing with a png here @@ -86,6 +88,18 @@ func terminateJpeg(scanner *bufio.Scanner, writer io.WriteCloser, expectedFileSi return nil } +func terminateWebp(scanner *bufio.Scanner, writer io.WriteCloser) error { + v := &webpVisitor{ + writer: writer, + } + + // use the webp visitor's 'split' function, which satisfies the bufio.SplitFunc interface + scanner.Split(v.split) + + scanAndClose(scanner, writer) + return nil +} + func terminatePng(scanner *bufio.Scanner, writer io.WriteCloser) error { ps := pngstructure.NewPngSplitter() diff --git a/vendor/github.com/superseriousbusiness/exif-terminator/webp.go b/vendor/github.com/superseriousbusiness/exif-terminator/webp.go @@ -0,0 +1,101 @@ +/* + exif-terminator + Copyright (C) 2022 SuperSeriousBusiness admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package terminator + +import ( + "encoding/binary" + "errors" + "io" +) + +const ( + riffHeaderSize = 4 * 3 +) + +var ( + riffHeader = [4]byte{'R', 'I', 'F', 'F'} + webpHeader = [4]byte{'W', 'E', 'B', 'P'} + exifFourcc = [4]byte{'E', 'X', 'I', 'F'} + xmpFourcc = [4]byte{'X', 'M', 'P', ' '} + + errNoRiffHeader = errors.New("no RIFF header") + errNoWebpHeader = errors.New("not a WEBP file") +) + +type webpVisitor struct { + writer io.Writer + doneHeader bool +} + +func fourCC(b []byte) [4]byte { + return [4]byte{b[0], b[1], b[2], b[3]} +} + +func (v *webpVisitor) split(data []byte, atEOF bool) (advance int, token []byte, err error) { + // parse/write the header first + if !v.doneHeader { + if len(data) < riffHeaderSize { + // need the full header + return + } + if fourCC(data) != riffHeader { + err = errNoRiffHeader + return + } + if fourCC(data[8:]) != webpHeader { + err = errNoWebpHeader + return + } + if _, err = v.writer.Write(data[:riffHeaderSize]); err != nil { + return + } + advance += riffHeaderSize + data = data[riffHeaderSize:] + v.doneHeader = true + } + + // need enough for fourcc and size + if len(data) < 8 { + return + } + size := int64(binary.LittleEndian.Uint32(data[4:])) + if (size & 1) != 0 { + // odd chunk size - extra padding byte + size++ + } + // wait until there is enough + if int64(len(data)-8) < size { + return + } + + fourcc := fourCC(data) + rawChunkData := data[8 : 8+size] + if fourcc == exifFourcc || fourcc == xmpFourcc { + // replace exif/xmp with blank + rawChunkData = make([]byte, size) + } + + if _, err = v.writer.Write(data[:8]); err == nil { + if _, err = v.writer.Write(rawChunkData); err == nil { + advance += 8 + int(size) + } + } + + return +} diff --git a/vendor/golang.org/x/image/riff/riff.go b/vendor/golang.org/x/image/riff/riff.go @@ -0,0 +1,193 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package riff implements the Resource Interchange File Format, used by media +// formats such as AVI, WAVE and WEBP. +// +// A RIFF stream contains a sequence of chunks. Each chunk consists of an 8-byte +// header (containing a 4-byte chunk type and a 4-byte chunk length), the chunk +// data (presented as an io.Reader), and some padding bytes. +// +// A detailed description of the format is at +// http://www.tactilemedia.com/info/MCI_Control_Info.html +package riff // import "golang.org/x/image/riff" + +import ( + "errors" + "io" + "io/ioutil" + "math" +) + +var ( + errMissingPaddingByte = errors.New("riff: missing padding byte") + errMissingRIFFChunkHeader = errors.New("riff: missing RIFF chunk header") + errListSubchunkTooLong = errors.New("riff: list subchunk too long") + errShortChunkData = errors.New("riff: short chunk data") + errShortChunkHeader = errors.New("riff: short chunk header") + errStaleReader = errors.New("riff: stale reader") +) + +// u32 decodes the first four bytes of b as a little-endian integer. +func u32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +const chunkHeaderSize = 8 + +// FourCC is a four character code. +type FourCC [4]byte + +// LIST is the "LIST" FourCC. +var LIST = FourCC{'L', 'I', 'S', 'T'} + +// NewReader returns the RIFF stream's form type, such as "AVI " or "WAVE", and +// its chunks as a *Reader. +func NewReader(r io.Reader) (formType FourCC, data *Reader, err error) { + var buf [chunkHeaderSize]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = errMissingRIFFChunkHeader + } + return FourCC{}, nil, err + } + if buf[0] != 'R' || buf[1] != 'I' || buf[2] != 'F' || buf[3] != 'F' { + return FourCC{}, nil, errMissingRIFFChunkHeader + } + return NewListReader(u32(buf[4:]), r) +} + +// NewListReader returns a LIST chunk's list type, such as "movi" or "wavl", +// and its chunks as a *Reader. +func NewListReader(chunkLen uint32, chunkData io.Reader) (listType FourCC, data *Reader, err error) { + if chunkLen < 4 { + return FourCC{}, nil, errShortChunkData + } + z := &Reader{r: chunkData} + if _, err := io.ReadFull(chunkData, z.buf[:4]); err != nil { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = errShortChunkData + } + return FourCC{}, nil, err + } + z.totalLen = chunkLen - 4 + return FourCC{z.buf[0], z.buf[1], z.buf[2], z.buf[3]}, z, nil +} + +// Reader reads chunks from an underlying io.Reader. +type Reader struct { + r io.Reader + err error + + totalLen uint32 + chunkLen uint32 + + chunkReader *chunkReader + buf [chunkHeaderSize]byte + padded bool +} + +// Next returns the next chunk's ID, length and data. It returns io.EOF if there +// are no more chunks. The io.Reader returned becomes stale after the next Next +// call, and should no longer be used. +// +// It is valid to call Next even if all of the previous chunk's data has not +// been read. +func (z *Reader) Next() (chunkID FourCC, chunkLen uint32, chunkData io.Reader, err error) { + if z.err != nil { + return FourCC{}, 0, nil, z.err + } + + // Drain the rest of the previous chunk. + if z.chunkLen != 0 { + want := z.chunkLen + var got int64 + got, z.err = io.Copy(ioutil.Discard, z.chunkReader) + if z.err == nil && uint32(got) != want { + z.err = errShortChunkData + } + if z.err != nil { + return FourCC{}, 0, nil, z.err + } + } + z.chunkReader = nil + if z.padded { + if z.totalLen == 0 { + z.err = errListSubchunkTooLong + return FourCC{}, 0, nil, z.err + } + z.totalLen-- + _, z.err = io.ReadFull(z.r, z.buf[:1]) + if z.err != nil { + if z.err == io.EOF { + z.err = errMissingPaddingByte + } + return FourCC{}, 0, nil, z.err + } + } + + // We are done if we have no more data. + if z.totalLen == 0 { + z.err = io.EOF + return FourCC{}, 0, nil, z.err + } + + // Read the next chunk header. + if z.totalLen < chunkHeaderSize { + z.err = errShortChunkHeader + return FourCC{}, 0, nil, z.err + } + z.totalLen -= chunkHeaderSize + if _, z.err = io.ReadFull(z.r, z.buf[:chunkHeaderSize]); z.err != nil { + if z.err == io.EOF || z.err == io.ErrUnexpectedEOF { + z.err = errShortChunkHeader + } + return FourCC{}, 0, nil, z.err + } + chunkID = FourCC{z.buf[0], z.buf[1], z.buf[2], z.buf[3]} + z.chunkLen = u32(z.buf[4:]) + if z.chunkLen > z.totalLen { + z.err = errListSubchunkTooLong + return FourCC{}, 0, nil, z.err + } + z.padded = z.chunkLen&1 == 1 + z.chunkReader = &chunkReader{z} + return chunkID, z.chunkLen, z.chunkReader, nil +} + +type chunkReader struct { + z *Reader +} + +func (c *chunkReader) Read(p []byte) (int, error) { + if c != c.z.chunkReader { + return 0, errStaleReader + } + z := c.z + if z.err != nil { + if z.err == io.EOF { + return 0, errStaleReader + } + return 0, z.err + } + + n := int(z.chunkLen) + if n == 0 { + return 0, io.EOF + } + if n < 0 { + // Converting uint32 to int overflowed. + n = math.MaxInt32 + } + if n > len(p) { + n = len(p) + } + n, err := z.r.Read(p[:n]) + z.totalLen -= uint32(n) + z.chunkLen -= uint32(n) + if err != io.EOF { + z.err = err + } + return n, err +} diff --git a/vendor/golang.org/x/image/vp8/decode.go b/vendor/golang.org/x/image/vp8/decode.go @@ -0,0 +1,403 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vp8 implements a decoder for the VP8 lossy image format. +// +// The VP8 specification is RFC 6386. +package vp8 // import "golang.org/x/image/vp8" + +// This file implements the top-level decoding algorithm. + +import ( + "errors" + "image" + "io" +) + +// limitReader wraps an io.Reader to read at most n bytes from it. +type limitReader struct { + r io.Reader + n int +} + +// ReadFull reads exactly len(p) bytes into p. +func (r *limitReader) ReadFull(p []byte) error { + if len(p) > r.n { + return io.ErrUnexpectedEOF + } + n, err := io.ReadFull(r.r, p) + r.n -= n + return err +} + +// FrameHeader is a frame header, as specified in section 9.1. +type FrameHeader struct { + KeyFrame bool + VersionNumber uint8 + ShowFrame bool + FirstPartitionLen uint32 + Width int + Height int + XScale uint8 + YScale uint8 +} + +const ( + nSegment = 4 + nSegmentProb = 3 +) + +// segmentHeader holds segment-related header information. +type segmentHeader struct { + useSegment bool + updateMap bool + relativeDelta bool + quantizer [nSegment]int8 + filterStrength [nSegment]int8 + prob [nSegmentProb]uint8 +} + +const ( + nRefLFDelta = 4 + nModeLFDelta = 4 +) + +// filterHeader holds filter-related header information. +type filterHeader struct { + simple bool + level int8 + sharpness uint8 + useLFDelta bool + refLFDelta [nRefLFDelta]int8 + modeLFDelta [nModeLFDelta]int8 + perSegmentLevel [nSegment]int8 +} + +// mb is the per-macroblock decode state. A decoder maintains mbw+1 of these +// as it is decoding macroblocks left-to-right and top-to-bottom: mbw for the +// macroblocks in the row above, and one for the macroblock to the left. +type mb struct { + // pred is the predictor mode for the 4 bottom or right 4x4 luma regions. + pred [4]uint8 + // nzMask is a mask of 8 bits: 4 for the bottom or right 4x4 luma regions, + // and 2 + 2 for the bottom or right 4x4 chroma regions. A 1 bit indicates + // that region has non-zero coefficients. + nzMask uint8 + // nzY16 is a 0/1 value that is 1 if the macroblock used Y16 prediction and + // had non-zero coefficients. + nzY16 uint8 +} + +// Decoder decodes VP8 bitstreams into frames. Decoding one frame consists of +// calling Init, DecodeFrameHeader and then DecodeFrame in that order. +// A Decoder can be re-used to decode multiple frames. +type Decoder struct { + // r is the input bitsream. + r limitReader + // scratch is a scratch buffer. + scratch [8]byte + // img is the YCbCr image to decode into. + img *image.YCbCr + // mbw and mbh are the number of 16x16 macroblocks wide and high the image is. + mbw, mbh int + // frameHeader is the frame header. When decoding multiple frames, + // frames that aren't key frames will inherit the Width, Height, + // XScale and YScale of the most recent key frame. + frameHeader FrameHeader + // Other headers. + segmentHeader segmentHeader + filterHeader filterHeader + // The image data is divided into a number of independent partitions. + // There is 1 "first partition" and between 1 and 8 "other partitions" + // for coefficient data. + fp partition + op [8]partition + nOP int + // Quantization factors. + quant [nSegment]quant + // DCT/WHT coefficient decoding probabilities. + tokenProb [nPlane][nBand][nContext][nProb]uint8 + useSkipProb bool + skipProb uint8 + // Loop filter parameters. + filterParams [nSegment][2]filterParam + perMBFilterParams []filterParam + + // The eight fields below relate to the current macroblock being decoded. + // + // Segment-based adjustments. + segment int + // Per-macroblock state for the macroblock immediately left of and those + // macroblocks immediately above the current macroblock. + leftMB mb + upMB []mb + // Bitmasks for which 4x4 regions of coeff contain non-zero coefficients. + nzDCMask, nzACMask uint32 + // Predictor modes. + usePredY16 bool // The libwebp C code calls this !is_i4x4_. + predY16 uint8 + predC8 uint8 + predY4 [4][4]uint8 + + // The two fields below form a workspace for reconstructing a macroblock. + // Their specific sizes are documented in reconstruct.go. + coeff [1*16*16 + 2*8*8 + 1*4*4]int16 + ybr [1 + 16 + 1 + 8][32]uint8 +} + +// NewDecoder returns a new Decoder. +func NewDecoder() *Decoder { + return &Decoder{} +} + +// Init initializes the decoder to read at most n bytes from r. +func (d *Decoder) Init(r io.Reader, n int) { + d.r = limitReader{r, n} +} + +// DecodeFrameHeader decodes the frame header. +func (d *Decoder) DecodeFrameHeader() (fh FrameHeader, err error) { + // All frame headers are at least 3 bytes long. + b := d.scratch[:3] + if err = d.r.ReadFull(b); err != nil { + return + } + d.frameHeader.KeyFrame = (b[0] & 1) == 0 + d.frameHeader.VersionNumber = (b[0] >> 1) & 7 + d.frameHeader.ShowFrame = (b[0]>>4)&1 == 1 + d.frameHeader.FirstPartitionLen = uint32(b[0])>>5 | uint32(b[1])<<3 | uint32(b[2])<<11 + if !d.frameHeader.KeyFrame { + return d.frameHeader, nil + } + // Frame headers for key frames are an additional 7 bytes long. + b = d.scratch[:7] + if err = d.r.ReadFull(b); err != nil { + return + } + // Check the magic sync code. + if b[0] != 0x9d || b[1] != 0x01 || b[2] != 0x2a { + err = errors.New("vp8: invalid format") + return + } + d.frameHeader.Width = int(b[4]&0x3f)<<8 | int(b[3]) + d.frameHeader.Height = int(b[6]&0x3f)<<8 | int(b[5]) + d.frameHeader.XScale = b[4] >> 6 + d.frameHeader.YScale = b[6] >> 6 + d.mbw = (d.frameHeader.Width + 0x0f) >> 4 + d.mbh = (d.frameHeader.Height + 0x0f) >> 4 + d.segmentHeader = segmentHeader{ + prob: [3]uint8{0xff, 0xff, 0xff}, + } + d.tokenProb = defaultTokenProb + d.segment = 0 + return d.frameHeader, nil +} + +// ensureImg ensures that d.img is large enough to hold the decoded frame. +func (d *Decoder) ensureImg() { + if d.img != nil { + p0, p1 := d.img.Rect.Min, d.img.Rect.Max + if p0.X == 0 && p0.Y == 0 && p1.X >= 16*d.mbw && p1.Y >= 16*d.mbh { + return + } + } + m := image.NewYCbCr(image.Rect(0, 0, 16*d.mbw, 16*d.mbh), image.YCbCrSubsampleRatio420) + d.img = m.SubImage(image.Rect(0, 0, d.frameHeader.Width, d.frameHeader.Height)).(*image.YCbCr) + d.perMBFilterParams = make([]filterParam, d.mbw*d.mbh) + d.upMB = make([]mb, d.mbw) +} + +// parseSegmentHeader parses the segment header, as specified in section 9.3. +func (d *Decoder) parseSegmentHeader() { + d.segmentHeader.useSegment = d.fp.readBit(uniformProb) + if !d.segmentHeader.useSegment { + d.segmentHeader.updateMap = false + return + } + d.segmentHeader.updateMap = d.fp.readBit(uniformProb) + if d.fp.readBit(uniformProb) { + d.segmentHeader.relativeDelta = !d.fp.readBit(uniformProb) + for i := range d.segmentHeader.quantizer { + d.segmentHeader.quantizer[i] = int8(d.fp.readOptionalInt(uniformProb, 7)) + } + for i := range d.segmentHeader.filterStrength { + d.segmentHeader.filterStrength[i] = int8(d.fp.readOptionalInt(uniformProb, 6)) + } + } + if !d.segmentHeader.updateMap { + return + } + for i := range d.segmentHeader.prob { + if d.fp.readBit(uniformProb) { + d.segmentHeader.prob[i] = uint8(d.fp.readUint(uniformProb, 8)) + } else { + d.segmentHeader.prob[i] = 0xff + } + } +} + +// parseFilterHeader parses the filter header, as specified in section 9.4. +func (d *Decoder) parseFilterHeader() { + d.filterHeader.simple = d.fp.readBit(uniformProb) + d.filterHeader.level = int8(d.fp.readUint(uniformProb, 6)) + d.filterHeader.sharpness = uint8(d.fp.readUint(uniformProb, 3)) + d.filterHeader.useLFDelta = d.fp.readBit(uniformProb) + if d.filterHeader.useLFDelta && d.fp.readBit(uniformProb) { + for i := range d.filterHeader.refLFDelta { + d.filterHeader.refLFDelta[i] = int8(d.fp.readOptionalInt(uniformProb, 6)) + } + for i := range d.filterHeader.modeLFDelta { + d.filterHeader.modeLFDelta[i] = int8(d.fp.readOptionalInt(uniformProb, 6)) + } + } + if d.filterHeader.level == 0 { + return + } + if d.segmentHeader.useSegment { + for i := range d.filterHeader.perSegmentLevel { + strength := d.segmentHeader.filterStrength[i] + if d.segmentHeader.relativeDelta { + strength += d.filterHeader.level + } + d.filterHeader.perSegmentLevel[i] = strength + } + } else { + d.filterHeader.perSegmentLevel[0] = d.filterHeader.level + } + d.computeFilterParams() +} + +// parseOtherPartitions parses the other partitions, as specified in section 9.5. +func (d *Decoder) parseOtherPartitions() error { + const maxNOP = 1 << 3 + var partLens [maxNOP]int + d.nOP = 1 << d.fp.readUint(uniformProb, 2) + + // The final partition length is implied by the remaining chunk data + // (d.r.n) and the other d.nOP-1 partition lengths. Those d.nOP-1 partition + // lengths are stored as 24-bit uints, i.e. up to 16 MiB per partition. + n := 3 * (d.nOP - 1) + partLens[d.nOP-1] = d.r.n - n + if partLens[d.nOP-1] < 0 { + return io.ErrUnexpectedEOF + } + if n > 0 { + buf := make([]byte, n) + if err := d.r.ReadFull(buf); err != nil { + return err + } + for i := 0; i < d.nOP-1; i++ { + pl := int(buf[3*i+0]) | int(buf[3*i+1])<<8 | int(buf[3*i+2])<<16 + if pl > partLens[d.nOP-1] { + return io.ErrUnexpectedEOF + } + partLens[i] = pl + partLens[d.nOP-1] -= pl + } + } + + // We check if the final partition length can also fit into a 24-bit uint. + // Strictly speaking, this isn't part of the spec, but it guards against a + // malicious WEBP image that is too large to ReadFull the encoded DCT + // coefficients into memory, whether that's because the actual WEBP file is + // too large, or whether its RIFF metadata lists too large a chunk. + if 1<<24 <= partLens[d.nOP-1] { + return errors.New("vp8: too much data to decode") + } + + buf := make([]byte, d.r.n) + if err := d.r.ReadFull(buf); err != nil { + return err + } + for i, pl := range partLens { + if i == d.nOP { + break + } + d.op[i].init(buf[:pl]) + buf = buf[pl:] + } + return nil +} + +// parseOtherHeaders parses header information other than the frame header. +func (d *Decoder) parseOtherHeaders() error { + // Initialize and parse the first partition. + firstPartition := make([]byte, d.frameHeader.FirstPartitionLen) + if err := d.r.ReadFull(firstPartition); err != nil { + return err + } + d.fp.init(firstPartition) + if d.frameHeader.KeyFrame { + // Read and ignore the color space and pixel clamp values. They are + // specified in section 9.2, but are unimplemented. + d.fp.readBit(uniformProb) + d.fp.readBit(uniformProb) + } + d.parseSegmentHeader() + d.parseFilterHeader() + if err := d.parseOtherPartitions(); err != nil { + return err + } + d.parseQuant() + if !d.frameHeader.KeyFrame { + // Golden and AltRef frames are specified in section 9.7. + // TODO(nigeltao): implement. Note that they are only used for video, not still images. + return errors.New("vp8: Golden / AltRef frames are not implemented") + } + // Read and ignore the refreshLastFrameBuffer bit, specified in section 9.8. + // It applies only to video, and not still images. + d.fp.readBit(uniformProb) + d.parseTokenProb() + d.useSkipProb = d.fp.readBit(uniformProb) + if d.useSkipProb { + d.skipProb = uint8(d.fp.readUint(uniformProb, 8)) + } + if d.fp.unexpectedEOF { + return io.ErrUnexpectedEOF + } + return nil +} + +// DecodeFrame decodes the frame and returns it as an YCbCr image. +// The image's contents are valid up until the next call to Decoder.Init. +func (d *Decoder) DecodeFrame() (*image.YCbCr, error) { + d.ensureImg() + if err := d.parseOtherHeaders(); err != nil { + return nil, err + } + // Reconstruct the rows. + for mbx := 0; mbx < d.mbw; mbx++ { + d.upMB[mbx] = mb{} + } + for mby := 0; mby < d.mbh; mby++ { + d.leftMB = mb{} + for mbx := 0; mbx < d.mbw; mbx++ { + skip := d.reconstruct(mbx, mby) + fs := d.filterParams[d.segment][btou(!d.usePredY16)] + fs.inner = fs.inner || !skip + d.perMBFilterParams[d.mbw*mby+mbx] = fs + } + } + if d.fp.unexpectedEOF { + return nil, io.ErrUnexpectedEOF + } + for i := 0; i < d.nOP; i++ { + if d.op[i].unexpectedEOF { + return nil, io.ErrUnexpectedEOF + } + } + // Apply the loop filter. + // + // Even if we are using per-segment levels, section 15 says that "loop + // filtering must be skipped entirely if loop_filter_level at either the + // frame header level or macroblock override level is 0". + if d.filterHeader.level != 0 { + if d.filterHeader.simple { + d.simpleFilter() + } else { + d.normalFilter() + } + } + return d.img, nil +} diff --git a/vendor/golang.org/x/image/vp8/filter.go b/vendor/golang.org/x/image/vp8/filter.go @@ -0,0 +1,273 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// filter2 modifies a 2-pixel wide or 2-pixel high band along an edge. +func filter2(pix []byte, level, index, iStep, jStep int) { + for n := 16; n > 0; n, index = n-1, index+iStep { + p1 := int(pix[index-2*jStep]) + p0 := int(pix[index-1*jStep]) + q0 := int(pix[index+0*jStep]) + q1 := int(pix[index+1*jStep]) + if abs(p0-q0)<<1+abs(p1-q1)>>1 > level { + continue + } + a := 3*(q0-p0) + clamp127(p1-q1) + a1 := clamp15((a + 4) >> 3) + a2 := clamp15((a + 3) >> 3) + pix[index-1*jStep] = clamp255(p0 + a2) + pix[index+0*jStep] = clamp255(q0 - a1) + } +} + +// filter246 modifies a 2-, 4- or 6-pixel wide or high band along an edge. +func filter246(pix []byte, n, level, ilevel, hlevel, index, iStep, jStep int, fourNotSix bool) { + for ; n > 0; n, index = n-1, index+iStep { + p3 := int(pix[index-4*jStep]) + p2 := int(pix[index-3*jStep]) + p1 := int(pix[index-2*jStep]) + p0 := int(pix[index-1*jStep]) + q0 := int(pix[index+0*jStep]) + q1 := int(pix[index+1*jStep]) + q2 := int(pix[index+2*jStep]) + q3 := int(pix[index+3*jStep]) + if abs(p0-q0)<<1+abs(p1-q1)>>1 > level { + continue + } + if abs(p3-p2) > ilevel || + abs(p2-p1) > ilevel || + abs(p1-p0) > ilevel || + abs(q1-q0) > ilevel || + abs(q2-q1) > ilevel || + abs(q3-q2) > ilevel { + continue + } + if abs(p1-p0) > hlevel || abs(q1-q0) > hlevel { + // Filter 2 pixels. + a := 3*(q0-p0) + clamp127(p1-q1) + a1 := clamp15((a + 4) >> 3) + a2 := clamp15((a + 3) >> 3) + pix[index-1*jStep] = clamp255(p0 + a2) + pix[index+0*jStep] = clamp255(q0 - a1) + } else if fourNotSix { + // Filter 4 pixels. + a := 3 * (q0 - p0) + a1 := clamp15((a + 4) >> 3) + a2 := clamp15((a + 3) >> 3) + a3 := (a1 + 1) >> 1 + pix[index-2*jStep] = clamp255(p1 + a3) + pix[index-1*jStep] = clamp255(p0 + a2) + pix[index+0*jStep] = clamp255(q0 - a1) + pix[index+1*jStep] = clamp255(q1 - a3) + } else { + // Filter 6 pixels. + a := clamp127(3*(q0-p0) + clamp127(p1-q1)) + a1 := (27*a + 63) >> 7 + a2 := (18*a + 63) >> 7 + a3 := (9*a + 63) >> 7 + pix[index-3*jStep] = clamp255(p2 + a3) + pix[index-2*jStep] = clamp255(p1 + a2) + pix[index-1*jStep] = clamp255(p0 + a1) + pix[index+0*jStep] = clamp255(q0 - a1) + pix[index+1*jStep] = clamp255(q1 - a2) + pix[index+2*jStep] = clamp255(q2 - a3) + } + } +} + +// simpleFilter implements the simple filter, as specified in section 15.2. +func (d *Decoder) simpleFilter() { + for mby := 0; mby < d.mbh; mby++ { + for mbx := 0; mbx < d.mbw; mbx++ { + f := d.perMBFilterParams[d.mbw*mby+mbx] + if f.level == 0 { + continue + } + l := int(f.level) + yIndex := (mby*d.img.YStride + mbx) * 16 + if mbx > 0 { + filter2(d.img.Y, l+4, yIndex, d.img.YStride, 1) + } + if f.inner { + filter2(d.img.Y, l, yIndex+0x4, d.img.YStride, 1) + filter2(d.img.Y, l, yIndex+0x8, d.img.YStride, 1) + filter2(d.img.Y, l, yIndex+0xc, d.img.YStride, 1) + } + if mby > 0 { + filter2(d.img.Y, l+4, yIndex, 1, d.img.YStride) + } + if f.inner { + filter2(d.img.Y, l, yIndex+d.img.YStride*0x4, 1, d.img.YStride) + filter2(d.img.Y, l, yIndex+d.img.YStride*0x8, 1, d.img.YStride) + filter2(d.img.Y, l, yIndex+d.img.YStride*0xc, 1, d.img.YStride) + } + } + } +} + +// normalFilter implements the normal filter, as specified in section 15.3. +func (d *Decoder) normalFilter() { + for mby := 0; mby < d.mbh; mby++ { + for mbx := 0; mbx < d.mbw; mbx++ { + f := d.perMBFilterParams[d.mbw*mby+mbx] + if f.level == 0 { + continue + } + l, il, hl := int(f.level), int(f.ilevel), int(f.hlevel) + yIndex := (mby*d.img.YStride + mbx) * 16 + cIndex := (mby*d.img.CStride + mbx) * 8 + if mbx > 0 { + filter246(d.img.Y, 16, l+4, il, hl, yIndex, d.img.YStride, 1, false) + filter246(d.img.Cb, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false) + filter246(d.img.Cr, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false) + } + if f.inner { + filter246(d.img.Y, 16, l, il, hl, yIndex+0x4, d.img.YStride, 1, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+0x8, d.img.YStride, 1, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+0xc, d.img.YStride, 1, true) + filter246(d.img.Cb, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true) + filter246(d.img.Cr, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true) + } + if mby > 0 { + filter246(d.img.Y, 16, l+4, il, hl, yIndex, 1, d.img.YStride, false) + filter246(d.img.Cb, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false) + filter246(d.img.Cr, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false) + } + if f.inner { + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x4, 1, d.img.YStride, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x8, 1, d.img.YStride, true) + filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0xc, 1, d.img.YStride, true) + filter246(d.img.Cb, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true) + filter246(d.img.Cr, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true) + } + } + } +} + +// filterParam holds the loop filter parameters for a macroblock. +type filterParam struct { + // The first three fields are thresholds used by the loop filter to smooth + // over the edges and interior of a macroblock. level is used by both the + // simple and normal filters. The inner level and high edge variance level + // are only used by the normal filter. + level, ilevel, hlevel uint8 + // inner is whether the inner loop filter cannot be optimized out as a + // no-op for this particular macroblock. + inner bool +} + +// computeFilterParams computes the loop filter parameters, as specified in +// section 15.4. +func (d *Decoder) computeFilterParams() { + for i := range d.filterParams { + baseLevel := d.filterHeader.level + if d.segmentHeader.useSegment { + baseLevel = d.segmentHeader.filterStrength[i] + if d.segmentHeader.relativeDelta { + baseLevel += d.filterHeader.level + } + } + + for j := range d.filterParams[i] { + p := &d.filterParams[i][j] + p.inner = j != 0 + level := baseLevel + if d.filterHeader.useLFDelta { + // The libwebp C code has a "TODO: only CURRENT is handled for now." + level += d.filterHeader.refLFDelta[0] + if j != 0 { + level += d.filterHeader.modeLFDelta[0] + } + } + if level <= 0 { + p.level = 0 + continue + } + if level > 63 { + level = 63 + } + ilevel := level + if d.filterHeader.sharpness > 0 { + if d.filterHeader.sharpness > 4 { + ilevel >>= 2 + } else { + ilevel >>= 1 + } + if x := int8(9 - d.filterHeader.sharpness); ilevel > x { + ilevel = x + } + } + if ilevel < 1 { + ilevel = 1 + } + p.ilevel = uint8(ilevel) + p.level = uint8(2*level + ilevel) + if d.frameHeader.KeyFrame { + if level < 15 { + p.hlevel = 0 + } else if level < 40 { + p.hlevel = 1 + } else { + p.hlevel = 2 + } + } else { + if level < 15 { + p.hlevel = 0 + } else if level < 20 { + p.hlevel = 1 + } else if level < 40 { + p.hlevel = 2 + } else { + p.hlevel = 3 + } + } + } + } +} + +// intSize is either 32 or 64. +const intSize = 32 << (^uint(0) >> 63) + +func abs(x int) int { + // m := -1 if x < 0. m := 0 otherwise. + m := x >> (intSize - 1) + + // In two's complement representation, the negative number + // of any number (except the smallest one) can be computed + // by flipping all the bits and add 1. This is faster than + // code with a branch. + // See Hacker's Delight, section 2-4. + return (x ^ m) - m +} + +func clamp15(x int) int { + if x < -16 { + return -16 + } + if x > 15 { + return 15 + } + return x +} + +func clamp127(x int) int { + if x < -128 { + return -128 + } + if x > 127 { + return 127 + } + return x +} + +func clamp255(x int) uint8 { + if x < 0 { + return 0 + } + if x > 255 { + return 255 + } + return uint8(x) +} diff --git a/vendor/golang.org/x/image/vp8/idct.go b/vendor/golang.org/x/image/vp8/idct.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file implements the inverse Discrete Cosine Transform and the inverse +// Walsh Hadamard Transform (WHT), as specified in sections 14.3 and 14.4. + +func clip8(i int32) uint8 { + if i < 0 { + return 0 + } + if i > 255 { + return 255 + } + return uint8(i) +} + +func (z *Decoder) inverseDCT4(y, x, coeffBase int) { + const ( + c1 = 85627 // 65536 * cos(pi/8) * sqrt(2). + c2 = 35468 // 65536 * sin(pi/8) * sqrt(2). + ) + var m [4][4]int32 + for i := 0; i < 4; i++ { + a := int32(z.coeff[coeffBase+0]) + int32(z.coeff[coeffBase+8]) + b := int32(z.coeff[coeffBase+0]) - int32(z.coeff[coeffBase+8]) + c := (int32(z.coeff[coeffBase+4])*c2)>>16 - (int32(z.coeff[coeffBase+12])*c1)>>16 + d := (int32(z.coeff[coeffBase+4])*c1)>>16 + (int32(z.coeff[coeffBase+12])*c2)>>16 + m[i][0] = a + d + m[i][1] = b + c + m[i][2] = b - c + m[i][3] = a - d + coeffBase++ + } + for j := 0; j < 4; j++ { + dc := m[0][j] + 4 + a := dc + m[2][j] + b := dc - m[2][j] + c := (m[1][j]*c2)>>16 - (m[3][j]*c1)>>16 + d := (m[1][j]*c1)>>16 + (m[3][j]*c2)>>16 + z.ybr[y+j][x+0] = clip8(int32(z.ybr[y+j][x+0]) + (a+d)>>3) + z.ybr[y+j][x+1] = clip8(int32(z.ybr[y+j][x+1]) + (b+c)>>3) + z.ybr[y+j][x+2] = clip8(int32(z.ybr[y+j][x+2]) + (b-c)>>3) + z.ybr[y+j][x+3] = clip8(int32(z.ybr[y+j][x+3]) + (a-d)>>3) + } +} + +func (z *Decoder) inverseDCT4DCOnly(y, x, coeffBase int) { + dc := (int32(z.coeff[coeffBase+0]) + 4) >> 3 + for j := 0; j < 4; j++ { + for i := 0; i < 4; i++ { + z.ybr[y+j][x+i] = clip8(int32(z.ybr[y+j][x+i]) + dc) + } + } +} + +func (z *Decoder) inverseDCT8(y, x, coeffBase int) { + z.inverseDCT4(y+0, x+0, coeffBase+0*16) + z.inverseDCT4(y+0, x+4, coeffBase+1*16) + z.inverseDCT4(y+4, x+0, coeffBase+2*16) + z.inverseDCT4(y+4, x+4, coeffBase+3*16) +} + +func (z *Decoder) inverseDCT8DCOnly(y, x, coeffBase int) { + z.inverseDCT4DCOnly(y+0, x+0, coeffBase+0*16) + z.inverseDCT4DCOnly(y+0, x+4, coeffBase+1*16) + z.inverseDCT4DCOnly(y+4, x+0, coeffBase+2*16) + z.inverseDCT4DCOnly(y+4, x+4, coeffBase+3*16) +} + +func (d *Decoder) inverseWHT16() { + var m [16]int32 + for i := 0; i < 4; i++ { + a0 := int32(d.coeff[384+0+i]) + int32(d.coeff[384+12+i]) + a1 := int32(d.coeff[384+4+i]) + int32(d.coeff[384+8+i]) + a2 := int32(d.coeff[384+4+i]) - int32(d.coeff[384+8+i]) + a3 := int32(d.coeff[384+0+i]) - int32(d.coeff[384+12+i]) + m[0+i] = a0 + a1 + m[8+i] = a0 - a1 + m[4+i] = a3 + a2 + m[12+i] = a3 - a2 + } + out := 0 + for i := 0; i < 4; i++ { + dc := m[0+i*4] + 3 + a0 := dc + m[3+i*4] + a1 := m[1+i*4] + m[2+i*4] + a2 := m[1+i*4] - m[2+i*4] + a3 := dc - m[3+i*4] + d.coeff[out+0] = int16((a0 + a1) >> 3) + d.coeff[out+16] = int16((a3 + a2) >> 3) + d.coeff[out+32] = int16((a0 - a1) >> 3) + d.coeff[out+48] = int16((a3 - a2) >> 3) + out += 64 + } +} diff --git a/vendor/golang.org/x/image/vp8/partition.go b/vendor/golang.org/x/image/vp8/partition.go @@ -0,0 +1,129 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// Each VP8 frame consists of between 2 and 9 bitstream partitions. +// Each partition is byte-aligned and is independently arithmetic-encoded. +// +// This file implements decoding a partition's bitstream, as specified in +// chapter 7. The implementation follows libwebp's approach instead of the +// specification's reference C implementation. For example, we use a look-up +// table instead of a for loop to recalibrate the encoded range. + +var ( + lutShift = [127]uint8{ + 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + } + lutRangeM1 = [127]uint8{ + 127, + 127, 191, + 127, 159, 191, 223, + 127, 143, 159, 175, 191, 207, 223, 239, + 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247, + 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187, + 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, + 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, + 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, + 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, + 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, + } +) + +// uniformProb represents a 50% probability that the next bit is 0. +const uniformProb = 128 + +// partition holds arithmetic-coded bits. +type partition struct { + // buf is the input bytes. + buf []byte + // r is how many of buf's bytes have been consumed. + r int + // rangeM1 is range minus 1, where range is in the arithmetic coding sense, + // not the Go language sense. + rangeM1 uint32 + // bits and nBits hold those bits shifted out of buf but not yet consumed. + bits uint32 + nBits uint8 + // unexpectedEOF tells whether we tried to read past buf. + unexpectedEOF bool +} + +// init initializes the partition. +func (p *partition) init(buf []byte) { + p.buf = buf + p.r = 0 + p.rangeM1 = 254 + p.bits = 0 + p.nBits = 0 + p.unexpectedEOF = false +} + +// readBit returns the next bit. +func (p *partition) readBit(prob uint8) bool { + if p.nBits < 8 { + if p.r >= len(p.buf) { + p.unexpectedEOF = true + return false + } + // Expression split for 386 compiler. + x := uint32(p.buf[p.r]) + p.bits |= x << (8 - p.nBits) + p.r++ + p.nBits += 8 + } + split := (p.rangeM1*uint32(prob))>>8 + 1 + bit := p.bits >= split<<8 + if bit { + p.rangeM1 -= split + p.bits -= split << 8 + } else { + p.rangeM1 = split - 1 + } + if p.rangeM1 < 127 { + shift := lutShift[p.rangeM1] + p.rangeM1 = uint32(lutRangeM1[p.rangeM1]) + p.bits <<= shift + p.nBits -= shift + } + return bit +} + +// readUint returns the next n-bit unsigned integer. +func (p *partition) readUint(prob, n uint8) uint32 { + var u uint32 + for n > 0 { + n-- + if p.readBit(prob) { + u |= 1 << n + } + } + return u +} + +// readInt returns the next n-bit signed integer. +func (p *partition) readInt(prob, n uint8) int32 { + u := p.readUint(prob, n) + b := p.readBit(prob) + if b { + return -int32(u) + } + return int32(u) +} + +// readOptionalInt returns the next n-bit signed integer in an encoding +// where the likely result is zero. +func (p *partition) readOptionalInt(prob, n uint8) int32 { + if !p.readBit(prob) { + return 0 + } + return p.readInt(prob, n) +} diff --git a/vendor/golang.org/x/image/vp8/pred.go b/vendor/golang.org/x/image/vp8/pred.go @@ -0,0 +1,201 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file implements parsing the predictor modes, as specified in chapter +// 11. + +func (d *Decoder) parsePredModeY16(mbx int) { + var p uint8 + if !d.fp.readBit(156) { + if !d.fp.readBit(163) { + p = predDC + } else { + p = predVE + } + } else if !d.fp.readBit(128) { + p = predHE + } else { + p = predTM + } + for i := 0; i < 4; i++ { + d.upMB[mbx].pred[i] = p + d.leftMB.pred[i] = p + } + d.predY16 = p +} + +func (d *Decoder) parsePredModeC8() { + if !d.fp.readBit(142) { + d.predC8 = predDC + } else if !d.fp.readBit(114) { + d.predC8 = predVE + } else if !d.fp.readBit(183) { + d.predC8 = predHE + } else { + d.predC8 = predTM + } +} + +func (d *Decoder) parsePredModeY4(mbx int) { + for j := 0; j < 4; j++ { + p := d.leftMB.pred[j] + for i := 0; i < 4; i++ { + prob := &predProb[d.upMB[mbx].pred[i]][p] + if !d.fp.readBit(prob[0]) { + p = predDC + } else if !d.fp.readBit(prob[1]) { + p = predTM + } else if !d.fp.readBit(prob[2]) { + p = predVE + } else if !d.fp.readBit(prob[3]) { + if !d.fp.readBit(prob[4]) { + p = predHE + } else if !d.fp.readBit(prob[5]) { + p = predRD + } else { + p = predVR + } + } else if !d.fp.readBit(prob[6]) { + p = predLD + } else if !d.fp.readBit(prob[7]) { + p = predVL + } else if !d.fp.readBit(prob[8]) { + p = predHD + } else { + p = predHU + } + d.predY4[j][i] = p + d.upMB[mbx].pred[i] = p + } + d.leftMB.pred[j] = p + } +} + +// predProb are the probabilities to decode a 4x4 region's predictor mode given +// the predictor modes of the regions above and left of it. +// These values are specified in section 11.5. +var predProb = [nPred][nPred][9]uint8{ + { + {231, 120, 48, 89, 115, 113, 120, 152, 112}, + {152, 179, 64, 126, 170, 118, 46, 70, 95}, + {175, 69, 143, 80, 85, 82, 72, 155, 103}, + {56, 58, 10, 171, 218, 189, 17, 13, 152}, + {114, 26, 17, 163, 44, 195, 21, 10, 173}, + {121, 24, 80, 195, 26, 62, 44, 64, 85}, + {144, 71, 10, 38, 171, 213, 144, 34, 26}, + {170, 46, 55, 19, 136, 160, 33, 206, 71}, + {63, 20, 8, 114, 114, 208, 12, 9, 226}, + {81, 40, 11, 96, 182, 84, 29, 16, 36}, + }, + { + {134, 183, 89, 137, 98, 101, 106, 165, 148}, + {72, 187, 100, 130, 157, 111, 32, 75, 80}, + {66, 102, 167, 99, 74, 62, 40, 234, 128}, + {41, 53, 9, 178, 241, 141, 26, 8, 107}, + {74, 43, 26, 146, 73, 166, 49, 23, 157}, + {65, 38, 105, 160, 51, 52, 31, 115, 128}, + {104, 79, 12, 27, 217, 255, 87, 17, 7}, + {87, 68, 71, 44, 114, 51, 15, 186, 23}, + {47, 41, 14, 110, 182, 183, 21, 17, 194}, + {66, 45, 25, 102, 197, 189, 23, 18, 22}, + }, + { + {88, 88, 147, 150, 42, 46, 45, 196, 205}, + {43, 97, 183, 117, 85, 38, 35, 179, 61}, + {39, 53, 200, 87, 26, 21, 43, 232, 171}, + {56, 34, 51, 104, 114, 102, 29, 93, 77}, + {39, 28, 85, 171, 58, 165, 90, 98, 64}, + {34, 22, 116, 206, 23, 34, 43, 166, 73}, + {107, 54, 32, 26, 51, 1, 81, 43, 31}, + {68, 25, 106, 22, 64, 171, 36, 225, 114}, + {34, 19, 21, 102, 132, 188, 16, 76, 124}, + {62, 18, 78, 95, 85, 57, 50, 48, 51}, + }, + { + {193, 101, 35, 159, 215, 111, 89, 46, 111}, + {60, 148, 31, 172, 219, 228, 21, 18, 111}, + {112, 113, 77, 85, 179, 255, 38, 120, 114}, + {40, 42, 1, 196, 245, 209, 10, 25, 109}, + {88, 43, 29, 140, 166, 213, 37, 43, 154}, + {61, 63, 30, 155, 67, 45, 68, 1, 209}, + {100, 80, 8, 43, 154, 1, 51, 26, 71}, + {142, 78, 78, 16, 255, 128, 34, 197, 171}, + {41, 40, 5, 102, 211, 183, 4, 1, 221}, + {51, 50, 17, 168, 209, 192, 23, 25, 82}, + }, + { + {138, 31, 36, 171, 27, 166, 38, 44, 229}, + {67, 87, 58, 169, 82, 115, 26, 59, 179}, + {63, 59, 90, 180, 59, 166, 93, 73, 154}, + {40, 40, 21, 116, 143, 209, 34, 39, 175}, + {47, 15, 16, 183, 34, 223, 49, 45, 183}, + {46, 17, 33, 183, 6, 98, 15, 32, 183}, + {57, 46, 22, 24, 128, 1, 54, 17, 37}, + {65, 32, 73, 115, 28, 128, 23, 128, 205}, + {40, 3, 9, 115, 51, 192, 18, 6, 223}, + {87, 37, 9, 115, 59, 77, 64, 21, 47}, + }, + { + {104, 55, 44, 218, 9, 54, 53, 130, 226}, + {64, 90, 70, 205, 40, 41, 23, 26, 57}, + {54, 57, 112, 184, 5, 41, 38, 166, 213}, + {30, 34, 26, 133, 152, 116, 10, 32, 134}, + {39, 19, 53, 221, 26, 114, 32, 73, 255}, + {31, 9, 65, 234, 2, 15, 1, 118, 73}, + {75, 32, 12, 51, 192, 255, 160, 43, 51}, + {88, 31, 35, 67, 102, 85, 55, 186, 85}, + {56, 21, 23, 111, 59, 205, 45, 37, 192}, + {55, 38, 70, 124, 73, 102, 1, 34, 98}, + }, + { + {125, 98, 42, 88, 104, 85, 117, 175, 82}, + {95, 84, 53, 89, 128, 100, 113, 101, 45}, + {75, 79, 123, 47, 51, 128, 81, 171, 1}, + {57, 17, 5, 71, 102, 57, 53, 41, 49}, + {38, 33, 13, 121, 57, 73, 26, 1, 85}, + {41, 10, 67, 138, 77, 110, 90, 47, 114}, + {115, 21, 2, 10, 102, 255, 166, 23, 6}, + {101, 29, 16, 10, 85, 128, 101, 196, 26}, + {57, 18, 10, 102, 102, 213, 34, 20, 43}, + {117, 20, 15, 36, 163, 128, 68, 1, 26}, + }, + { + {102, 61, 71, 37, 34, 53, 31, 243, 192}, + {69, 60, 71, 38, 73, 119, 28, 222, 37}, + {68, 45, 128, 34, 1, 47, 11, 245, 171}, + {62, 17, 19, 70, 146, 85, 55, 62, 70}, + {37, 43, 37, 154, 100, 163, 85, 160, 1}, + {63, 9, 92, 136, 28, 64, 32, 201, 85}, + {75, 15, 9, 9, 64, 255, 184, 119, 16}, + {86, 6, 28, 5, 64, 255, 25, 248, 1}, + {56, 8, 17, 132, 137, 255, 55, 116, 128}, + {58, 15, 20, 82, 135, 57, 26, 121, 40}, + }, + { + {164, 50, 31, 137, 154, 133, 25, 35, 218}, + {51, 103, 44, 131, 131, 123, 31, 6, 158}, + {86, 40, 64, 135, 148, 224, 45, 183, 128}, + {22, 26, 17, 131, 240, 154, 14, 1, 209}, + {45, 16, 21, 91, 64, 222, 7, 1, 197}, + {56, 21, 39, 155, 60, 138, 23, 102, 213}, + {83, 12, 13, 54, 192, 255, 68, 47, 28}, + {85, 26, 85, 85, 128, 128, 32, 146, 171}, + {18, 11, 7, 63, 144, 171, 4, 4, 246}, + {35, 27, 10, 146, 174, 171, 12, 26, 128}, + }, + { + {190, 80, 35, 99, 180, 80, 126, 54, 45}, + {85, 126, 47, 87, 176, 51, 41, 20, 32}, + {101, 75, 128, 139, 118, 146, 116, 128, 85}, + {56, 41, 15, 176, 236, 85, 37, 9, 62}, + {71, 30, 17, 119, 118, 255, 17, 18, 138}, + {101, 38, 60, 138, 55, 70, 43, 26, 142}, + {146, 36, 19, 30, 171, 255, 97, 27, 20}, + {138, 45, 61, 62, 219, 1, 81, 188, 64}, + {32, 41, 20, 117, 151, 142, 20, 21, 163}, + {112, 19, 12, 61, 195, 128, 48, 4, 24}, + }, +} diff --git a/vendor/golang.org/x/image/vp8/predfunc.go b/vendor/golang.org/x/image/vp8/predfunc.go @@ -0,0 +1,553 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file implements the predicition functions, as specified in chapter 12. +// +// For each macroblock (of 1x16x16 luma and 2x8x8 chroma coefficients), the +// luma values are either predicted as one large 16x16 region or 16 separate +// 4x4 regions. The chroma values are always predicted as one 8x8 region. +// +// For 4x4 regions, the target block's predicted values (Xs) are a function of +// its previously-decoded top and left border values, as well as a number of +// pixels from the top-right: +// +// a b c d e f g h +// p X X X X +// q X X X X +// r X X X X +// s X X X X +// +// The predictor modes are: +// - DC: all Xs = (b + c + d + e + p + q + r + s + 4) / 8. +// - TM: the first X = (b + p - a), the second X = (c + p - a), and so on. +// - VE: each X = the weighted average of its column's top value and that +// value's neighbors, i.e. averages of abc, bcd, cde or def. +// - HE: similar to VE except rows instead of columns, and the final row is +// an average of r, s and s. +// - RD, VR, LD, VL, HD, HU: these diagonal modes ("Right Down", "Vertical +// Right", etc) are more complicated and are described in section 12.3. +// All Xs are clipped to the range [0, 255]. +// +// For 8x8 and 16x16 regions, the target block's predicted values are a +// function of the top and left border values without the top-right overhang, +// i.e. without the 8x8 or 16x16 equivalent of f, g and h. Furthermore: +// - There are no diagonal predictor modes, only DC, TM, VE and HE. +// - The DC mode has variants for macroblocks in the top row and/or left +// column, i.e. for macroblocks with mby == 0 || mbx == 0. +// - The VE and HE modes take only the column top or row left values; they do +// not smooth that top/left value with its neighbors. + +// nPred is the number of predictor modes, not including the Top/Left versions +// of the DC predictor mode. +const nPred = 10 + +const ( + predDC = iota + predTM + predVE + predHE + predRD + predVR + predLD + predVL + predHD + predHU + predDCTop + predDCLeft + predDCTopLeft +) + +func checkTopLeftPred(mbx, mby int, p uint8) uint8 { + if p != predDC { + return p + } + if mbx == 0 { + if mby == 0 { + return predDCTopLeft + } + return predDCLeft + } + if mby == 0 { + return predDCTop + } + return predDC +} + +var predFunc4 = [...]func(*Decoder, int, int){ + predFunc4DC, + predFunc4TM, + predFunc4VE, + predFunc4HE, + predFunc4RD, + predFunc4VR, + predFunc4LD, + predFunc4VL, + predFunc4HD, + predFunc4HU, + nil, + nil, + nil, +} + +var predFunc8 = [...]func(*Decoder, int, int){ + predFunc8DC, + predFunc8TM, + predFunc8VE, + predFunc8HE, + nil, + nil, + nil, + nil, + nil, + nil, + predFunc8DCTop, + predFunc8DCLeft, + predFunc8DCTopLeft, +} + +var predFunc16 = [...]func(*Decoder, int, int){ + predFunc16DC, + predFunc16TM, + predFunc16VE, + predFunc16HE, + nil, + nil, + nil, + nil, + nil, + nil, + predFunc16DCTop, + predFunc16DCLeft, + predFunc16DCTopLeft, +} + +func predFunc4DC(z *Decoder, y, x int) { + sum := uint32(4) + for i := 0; i < 4; i++ { + sum += uint32(z.ybr[y-1][x+i]) + } + for j := 0; j < 4; j++ { + sum += uint32(z.ybr[y+j][x-1]) + } + avg := uint8(sum / 8) + for j := 0; j < 4; j++ { + for i := 0; i < 4; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc4TM(z *Decoder, y, x int) { + delta0 := -int32(z.ybr[y-1][x-1]) + for j := 0; j < 4; j++ { + delta1 := delta0 + int32(z.ybr[y+j][x-1]) + for i := 0; i < 4; i++ { + delta2 := delta1 + int32(z.ybr[y-1][x+i]) + z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255)) + } + } +} + +func predFunc4VE(z *Decoder, y, x int) { + a := int32(z.ybr[y-1][x-1]) + b := int32(z.ybr[y-1][x+0]) + c := int32(z.ybr[y-1][x+1]) + d := int32(z.ybr[y-1][x+2]) + e := int32(z.ybr[y-1][x+3]) + f := int32(z.ybr[y-1][x+4]) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + cde := uint8((c + 2*d + e + 2) / 4) + def := uint8((d + 2*e + f + 2) / 4) + for j := 0; j < 4; j++ { + z.ybr[y+j][x+0] = abc + z.ybr[y+j][x+1] = bcd + z.ybr[y+j][x+2] = cde + z.ybr[y+j][x+3] = def + } +} + +func predFunc4HE(z *Decoder, y, x int) { + s := int32(z.ybr[y+3][x-1]) + r := int32(z.ybr[y+2][x-1]) + q := int32(z.ybr[y+1][x-1]) + p := int32(z.ybr[y+0][x-1]) + a := int32(z.ybr[y-1][x-1]) + ssr := uint8((s + 2*s + r + 2) / 4) + srq := uint8((s + 2*r + q + 2) / 4) + rqp := uint8((r + 2*q + p + 2) / 4) + apq := uint8((a + 2*p + q + 2) / 4) + for i := 0; i < 4; i++ { + z.ybr[y+0][x+i] = apq + z.ybr[y+1][x+i] = rqp + z.ybr[y+2][x+i] = srq + z.ybr[y+3][x+i] = ssr + } +} + +func predFunc4RD(z *Decoder, y, x int) { + s := int32(z.ybr[y+3][x-1]) + r := int32(z.ybr[y+2][x-1]) + q := int32(z.ybr[y+1][x-1]) + p := int32(z.ybr[y+0][x-1]) + a := int32(z.ybr[y-1][x-1]) + b := int32(z.ybr[y-1][x+0]) + c := int32(z.ybr[y-1][x+1]) + d := int32(z.ybr[y-1][x+2]) + e := int32(z.ybr[y-1][x+3]) + srq := uint8((s + 2*r + q + 2) / 4) + rqp := uint8((r + 2*q + p + 2) / 4) + qpa := uint8((q + 2*p + a + 2) / 4) + pab := uint8((p + 2*a + b + 2) / 4) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + cde := uint8((c + 2*d + e + 2) / 4) + z.ybr[y+0][x+0] = pab + z.ybr[y+0][x+1] = abc + z.ybr[y+0][x+2] = bcd + z.ybr[y+0][x+3] = cde + z.ybr[y+1][x+0] = qpa + z.ybr[y+1][x+1] = pab + z.ybr[y+1][x+2] = abc + z.ybr[y+1][x+3] = bcd + z.ybr[y+2][x+0] = rqp + z.ybr[y+2][x+1] = qpa + z.ybr[y+2][x+2] = pab + z.ybr[y+2][x+3] = abc + z.ybr[y+3][x+0] = srq + z.ybr[y+3][x+1] = rqp + z.ybr[y+3][x+2] = qpa + z.ybr[y+3][x+3] = pab +} + +func predFunc4VR(z *Decoder, y, x int) { + r := int32(z.ybr[y+2][x-1]) + q := int32(z.ybr[y+1][x-1]) + p := int32(z.ybr[y+0][x-1]) + a := int32(z.ybr[y-1][x-1]) + b := int32(z.ybr[y-1][x+0]) + c := int32(z.ybr[y-1][x+1]) + d := int32(z.ybr[y-1][x+2]) + e := int32(z.ybr[y-1][x+3]) + ab := uint8((a + b + 1) / 2) + bc := uint8((b + c + 1) / 2) + cd := uint8((c + d + 1) / 2) + de := uint8((d + e + 1) / 2) + rqp := uint8((r + 2*q + p + 2) / 4) + qpa := uint8((q + 2*p + a + 2) / 4) + pab := uint8((p + 2*a + b + 2) / 4) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + cde := uint8((c + 2*d + e + 2) / 4) + z.ybr[y+0][x+0] = ab + z.ybr[y+0][x+1] = bc + z.ybr[y+0][x+2] = cd + z.ybr[y+0][x+3] = de + z.ybr[y+1][x+0] = pab + z.ybr[y+1][x+1] = abc + z.ybr[y+1][x+2] = bcd + z.ybr[y+1][x+3] = cde + z.ybr[y+2][x+0] = qpa + z.ybr[y+2][x+1] = ab + z.ybr[y+2][x+2] = bc + z.ybr[y+2][x+3] = cd + z.ybr[y+3][x+0] = rqp + z.ybr[y+3][x+1] = pab + z.ybr[y+3][x+2] = abc + z.ybr[y+3][x+3] = bcd +} + +func predFunc4LD(z *Decoder, y, x int) { + a := int32(z.ybr[y-1][x+0]) + b := int32(z.ybr[y-1][x+1]) + c := int32(z.ybr[y-1][x+2]) + d := int32(z.ybr[y-1][x+3]) + e := int32(z.ybr[y-1][x+4]) + f := int32(z.ybr[y-1][x+5]) + g := int32(z.ybr[y-1][x+6]) + h := int32(z.ybr[y-1][x+7]) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + cde := uint8((c + 2*d + e + 2) / 4) + def := uint8((d + 2*e + f + 2) / 4) + efg := uint8((e + 2*f + g + 2) / 4) + fgh := uint8((f + 2*g + h + 2) / 4) + ghh := uint8((g + 2*h + h + 2) / 4) + z.ybr[y+0][x+0] = abc + z.ybr[y+0][x+1] = bcd + z.ybr[y+0][x+2] = cde + z.ybr[y+0][x+3] = def + z.ybr[y+1][x+0] = bcd + z.ybr[y+1][x+1] = cde + z.ybr[y+1][x+2] = def + z.ybr[y+1][x+3] = efg + z.ybr[y+2][x+0] = cde + z.ybr[y+2][x+1] = def + z.ybr[y+2][x+2] = efg + z.ybr[y+2][x+3] = fgh + z.ybr[y+3][x+0] = def + z.ybr[y+3][x+1] = efg + z.ybr[y+3][x+2] = fgh + z.ybr[y+3][x+3] = ghh +} + +func predFunc4VL(z *Decoder, y, x int) { + a := int32(z.ybr[y-1][x+0]) + b := int32(z.ybr[y-1][x+1]) + c := int32(z.ybr[y-1][x+2]) + d := int32(z.ybr[y-1][x+3]) + e := int32(z.ybr[y-1][x+4]) + f := int32(z.ybr[y-1][x+5]) + g := int32(z.ybr[y-1][x+6]) + h := int32(z.ybr[y-1][x+7]) + ab := uint8((a + b + 1) / 2) + bc := uint8((b + c + 1) / 2) + cd := uint8((c + d + 1) / 2) + de := uint8((d + e + 1) / 2) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + cde := uint8((c + 2*d + e + 2) / 4) + def := uint8((d + 2*e + f + 2) / 4) + efg := uint8((e + 2*f + g + 2) / 4) + fgh := uint8((f + 2*g + h + 2) / 4) + z.ybr[y+0][x+0] = ab + z.ybr[y+0][x+1] = bc + z.ybr[y+0][x+2] = cd + z.ybr[y+0][x+3] = de + z.ybr[y+1][x+0] = abc + z.ybr[y+1][x+1] = bcd + z.ybr[y+1][x+2] = cde + z.ybr[y+1][x+3] = def + z.ybr[y+2][x+0] = bc + z.ybr[y+2][x+1] = cd + z.ybr[y+2][x+2] = de + z.ybr[y+2][x+3] = efg + z.ybr[y+3][x+0] = bcd + z.ybr[y+3][x+1] = cde + z.ybr[y+3][x+2] = def + z.ybr[y+3][x+3] = fgh +} + +func predFunc4HD(z *Decoder, y, x int) { + s := int32(z.ybr[y+3][x-1]) + r := int32(z.ybr[y+2][x-1]) + q := int32(z.ybr[y+1][x-1]) + p := int32(z.ybr[y+0][x-1]) + a := int32(z.ybr[y-1][x-1]) + b := int32(z.ybr[y-1][x+0]) + c := int32(z.ybr[y-1][x+1]) + d := int32(z.ybr[y-1][x+2]) + sr := uint8((s + r + 1) / 2) + rq := uint8((r + q + 1) / 2) + qp := uint8((q + p + 1) / 2) + pa := uint8((p + a + 1) / 2) + srq := uint8((s + 2*r + q + 2) / 4) + rqp := uint8((r + 2*q + p + 2) / 4) + qpa := uint8((q + 2*p + a + 2) / 4) + pab := uint8((p + 2*a + b + 2) / 4) + abc := uint8((a + 2*b + c + 2) / 4) + bcd := uint8((b + 2*c + d + 2) / 4) + z.ybr[y+0][x+0] = pa + z.ybr[y+0][x+1] = pab + z.ybr[y+0][x+2] = abc + z.ybr[y+0][x+3] = bcd + z.ybr[y+1][x+0] = qp + z.ybr[y+1][x+1] = qpa + z.ybr[y+1][x+2] = pa + z.ybr[y+1][x+3] = pab + z.ybr[y+2][x+0] = rq + z.ybr[y+2][x+1] = rqp + z.ybr[y+2][x+2] = qp + z.ybr[y+2][x+3] = qpa + z.ybr[y+3][x+0] = sr + z.ybr[y+3][x+1] = srq + z.ybr[y+3][x+2] = rq + z.ybr[y+3][x+3] = rqp +} + +func predFunc4HU(z *Decoder, y, x int) { + s := int32(z.ybr[y+3][x-1]) + r := int32(z.ybr[y+2][x-1]) + q := int32(z.ybr[y+1][x-1]) + p := int32(z.ybr[y+0][x-1]) + pq := uint8((p + q + 1) / 2) + qr := uint8((q + r + 1) / 2) + rs := uint8((r + s + 1) / 2) + pqr := uint8((p + 2*q + r + 2) / 4) + qrs := uint8((q + 2*r + s + 2) / 4) + rss := uint8((r + 2*s + s + 2) / 4) + sss := uint8(s) + z.ybr[y+0][x+0] = pq + z.ybr[y+0][x+1] = pqr + z.ybr[y+0][x+2] = qr + z.ybr[y+0][x+3] = qrs + z.ybr[y+1][x+0] = qr + z.ybr[y+1][x+1] = qrs + z.ybr[y+1][x+2] = rs + z.ybr[y+1][x+3] = rss + z.ybr[y+2][x+0] = rs + z.ybr[y+2][x+1] = rss + z.ybr[y+2][x+2] = sss + z.ybr[y+2][x+3] = sss + z.ybr[y+3][x+0] = sss + z.ybr[y+3][x+1] = sss + z.ybr[y+3][x+2] = sss + z.ybr[y+3][x+3] = sss +} + +func predFunc8DC(z *Decoder, y, x int) { + sum := uint32(8) + for i := 0; i < 8; i++ { + sum += uint32(z.ybr[y-1][x+i]) + } + for j := 0; j < 8; j++ { + sum += uint32(z.ybr[y+j][x-1]) + } + avg := uint8(sum / 16) + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc8TM(z *Decoder, y, x int) { + delta0 := -int32(z.ybr[y-1][x-1]) + for j := 0; j < 8; j++ { + delta1 := delta0 + int32(z.ybr[y+j][x-1]) + for i := 0; i < 8; i++ { + delta2 := delta1 + int32(z.ybr[y-1][x+i]) + z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255)) + } + } +} + +func predFunc8VE(z *Decoder, y, x int) { + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = z.ybr[y-1][x+i] + } + } +} + +func predFunc8HE(z *Decoder, y, x int) { + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = z.ybr[y+j][x-1] + } + } +} + +func predFunc8DCTop(z *Decoder, y, x int) { + sum := uint32(4) + for j := 0; j < 8; j++ { + sum += uint32(z.ybr[y+j][x-1]) + } + avg := uint8(sum / 8) + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc8DCLeft(z *Decoder, y, x int) { + sum := uint32(4) + for i := 0; i < 8; i++ { + sum += uint32(z.ybr[y-1][x+i]) + } + avg := uint8(sum / 8) + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc8DCTopLeft(z *Decoder, y, x int) { + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + z.ybr[y+j][x+i] = 0x80 + } + } +} + +func predFunc16DC(z *Decoder, y, x int) { + sum := uint32(16) + for i := 0; i < 16; i++ { + sum += uint32(z.ybr[y-1][x+i]) + } + for j := 0; j < 16; j++ { + sum += uint32(z.ybr[y+j][x-1]) + } + avg := uint8(sum / 32) + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc16TM(z *Decoder, y, x int) { + delta0 := -int32(z.ybr[y-1][x-1]) + for j := 0; j < 16; j++ { + delta1 := delta0 + int32(z.ybr[y+j][x-1]) + for i := 0; i < 16; i++ { + delta2 := delta1 + int32(z.ybr[y-1][x+i]) + z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255)) + } + } +} + +func predFunc16VE(z *Decoder, y, x int) { + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = z.ybr[y-1][x+i] + } + } +} + +func predFunc16HE(z *Decoder, y, x int) { + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = z.ybr[y+j][x-1] + } + } +} + +func predFunc16DCTop(z *Decoder, y, x int) { + sum := uint32(8) + for j := 0; j < 16; j++ { + sum += uint32(z.ybr[y+j][x-1]) + } + avg := uint8(sum / 16) + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc16DCLeft(z *Decoder, y, x int) { + sum := uint32(8) + for i := 0; i < 16; i++ { + sum += uint32(z.ybr[y-1][x+i]) + } + avg := uint8(sum / 16) + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = avg + } + } +} + +func predFunc16DCTopLeft(z *Decoder, y, x int) { + for j := 0; j < 16; j++ { + for i := 0; i < 16; i++ { + z.ybr[y+j][x+i] = 0x80 + } + } +} diff --git a/vendor/golang.org/x/image/vp8/quant.go b/vendor/golang.org/x/image/vp8/quant.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file implements parsing the quantization factors. + +// quant are DC/AC quantization factors. +type quant struct { + y1 [2]uint16 + y2 [2]uint16 + uv [2]uint16 +} + +// clip clips x to the range [min, max] inclusive. +func clip(x, min, max int32) int32 { + if x < min { + return min + } + if x > max { + return max + } + return x +} + +// parseQuant parses the quantization factors, as specified in section 9.6. +func (d *Decoder) parseQuant() { + baseQ0 := d.fp.readUint(uniformProb, 7) + dqy1DC := d.fp.readOptionalInt(uniformProb, 4) + const dqy1AC = 0 + dqy2DC := d.fp.readOptionalInt(uniformProb, 4) + dqy2AC := d.fp.readOptionalInt(uniformProb, 4) + dquvDC := d.fp.readOptionalInt(uniformProb, 4) + dquvAC := d.fp.readOptionalInt(uniformProb, 4) + for i := 0; i < nSegment; i++ { + q := int32(baseQ0) + if d.segmentHeader.useSegment { + if d.segmentHeader.relativeDelta { + q += int32(d.segmentHeader.quantizer[i]) + } else { + q = int32(d.segmentHeader.quantizer[i]) + } + } + d.quant[i].y1[0] = dequantTableDC[clip(q+dqy1DC, 0, 127)] + d.quant[i].y1[1] = dequantTableAC[clip(q+dqy1AC, 0, 127)] + d.quant[i].y2[0] = dequantTableDC[clip(q+dqy2DC, 0, 127)] * 2 + d.quant[i].y2[1] = dequantTableAC[clip(q+dqy2AC, 0, 127)] * 155 / 100 + if d.quant[i].y2[1] < 8 { + d.quant[i].y2[1] = 8 + } + // The 117 is not a typo. The dequant_init function in the spec's Reference + // Decoder Source Code (http://tools.ietf.org/html/rfc6386#section-9.6 Page 145) + // says to clamp the LHS value at 132, which is equal to dequantTableDC[117]. + d.quant[i].uv[0] = dequantTableDC[clip(q+dquvDC, 0, 117)] + d.quant[i].uv[1] = dequantTableAC[clip(q+dquvAC, 0, 127)] + } +} + +// The dequantization tables are specified in section 14.1. +var ( + dequantTableDC = [128]uint16{ + 4, 5, 6, 7, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 16, 17, 17, + 18, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, + 91, 93, 95, 96, 98, 100, 101, 102, + 104, 106, 108, 110, 112, 114, 116, 118, + 122, 124, 126, 128, 130, 132, 134, 136, + 138, 140, 143, 145, 148, 151, 154, 157, + } + dequantTableAC = [128]uint16{ + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 60, + 62, 64, 66, 68, 70, 72, 74, 76, + 78, 80, 82, 84, 86, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, + 110, 112, 114, 116, 119, 122, 125, 128, + 131, 134, 137, 140, 143, 146, 149, 152, + 155, 158, 161, 164, 167, 170, 173, 177, + 181, 185, 189, 193, 197, 201, 205, 209, + 213, 217, 221, 225, 229, 234, 239, 245, + 249, 254, 259, 264, 269, 274, 279, 284, + } +) diff --git a/vendor/golang.org/x/image/vp8/reconstruct.go b/vendor/golang.org/x/image/vp8/reconstruct.go @@ -0,0 +1,442 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file implements decoding DCT/WHT residual coefficients and +// reconstructing YCbCr data equal to predicted values plus residuals. +// +// There are 1*16*16 + 2*8*8 + 1*4*4 coefficients per macroblock: +// - 1*16*16 luma DCT coefficients, +// - 2*8*8 chroma DCT coefficients, and +// - 1*4*4 luma WHT coefficients. +// Coefficients are read in lots of 16, and the later coefficients in each lot +// are often zero. +// +// The YCbCr data consists of 1*16*16 luma values and 2*8*8 chroma values, +// plus previously decoded values along the top and left borders. The combined +// values are laid out as a [1+16+1+8][32]uint8 so that vertically adjacent +// samples are 32 bytes apart. In detail, the layout is: +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// . . . . . . . a b b b b b b b b b b b b b b b b c c c c . . . . 0 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 1 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 2 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 3 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 4 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 5 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 6 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 7 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 8 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 9 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 10 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 11 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 12 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 13 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 14 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 15 +// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 16 +// . . . . . . . e f f f f f f f f . . . . . . . g h h h h h h h h 17 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 18 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 19 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 20 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 21 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 22 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 23 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 24 +// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 25 +// +// Y, B and R are the reconstructed luma (Y) and chroma (B, R) values. +// The Y values are predicted (either as one 16x16 region or 16 4x4 regions) +// based on the row above's Y values (some combination of {abc} or {dYC}) and +// the column left's Y values (either {ad} or {bY}). Similarly, B and R values +// are predicted on the row above and column left of their respective 8x8 +// region: {efi} for B, {ghj} for R. +// +// For uppermost macroblocks (i.e. those with mby == 0), the {abcefgh} values +// are initialized to 0x81. Otherwise, they are copied from the bottom row of +// the macroblock above. The {c} values are then duplicated from row 0 to rows +// 4, 8 and 12 of the ybr workspace. +// Similarly, for leftmost macroblocks (i.e. those with mbx == 0), the {adeigj} +// values are initialized to 0x7f. Otherwise, they are copied from the right +// column of the macroblock to the left. +// For the top-left macroblock (with mby == 0 && mbx == 0), {aeg} is 0x81. +// +// When moving from one macroblock to the next horizontally, the {adeigj} +// values can simply be copied from the workspace to itself, shifted by 8 or +// 16 columns. When moving from one macroblock to the next vertically, +// filtering can occur and hence the row values have to be copied from the +// post-filtered image instead of the pre-filtered workspace. + +const ( + bCoeffBase = 1*16*16 + 0*8*8 + rCoeffBase = 1*16*16 + 1*8*8 + whtCoeffBase = 1*16*16 + 2*8*8 +) + +const ( + ybrYX = 8 + ybrYY = 1 + ybrBX = 8 + ybrBY = 18 + ybrRX = 24 + ybrRY = 18 +) + +// prepareYBR prepares the {abcdefghij} elements of ybr. +func (d *Decoder) prepareYBR(mbx, mby int) { + if mbx == 0 { + for y := 0; y < 17; y++ { + d.ybr[y][7] = 0x81 + } + for y := 17; y < 26; y++ { + d.ybr[y][7] = 0x81 + d.ybr[y][23] = 0x81 + } + } else { + for y := 0; y < 17; y++ { + d.ybr[y][7] = d.ybr[y][7+16] + } + for y := 17; y < 26; y++ { + d.ybr[y][7] = d.ybr[y][15] + d.ybr[y][23] = d.ybr[y][31] + } + } + if mby == 0 { + for x := 7; x < 28; x++ { + d.ybr[0][x] = 0x7f + } + for x := 7; x < 16; x++ { + d.ybr[17][x] = 0x7f + } + for x := 23; x < 32; x++ { + d.ybr[17][x] = 0x7f + } + } else { + for i := 0; i < 16; i++ { + d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+i] + } + for i := 0; i < 8; i++ { + d.ybr[17][8+i] = d.img.Cb[(8*mby-1)*d.img.CStride+8*mbx+i] + } + for i := 0; i < 8; i++ { + d.ybr[17][24+i] = d.img.Cr[(8*mby-1)*d.img.CStride+8*mbx+i] + } + if mbx == d.mbw-1 { + for i := 16; i < 20; i++ { + d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+15] + } + } else { + for i := 16; i < 20; i++ { + d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+i] + } + } + } + for y := 4; y < 16; y += 4 { + d.ybr[y][24] = d.ybr[0][24] + d.ybr[y][25] = d.ybr[0][25] + d.ybr[y][26] = d.ybr[0][26] + d.ybr[y][27] = d.ybr[0][27] + } +} + +// btou converts a bool to a 0/1 value. +func btou(b bool) uint8 { + if b { + return 1 + } + return 0 +} + +// pack packs four 0/1 values into four bits of a uint32. +func pack(x [4]uint8, shift int) uint32 { + u := uint32(x[0])<<0 | uint32(x[1])<<1 | uint32(x[2])<<2 | uint32(x[3])<<3 + return u << uint(shift) +} + +// unpack unpacks four 0/1 values from a four-bit value. +var unpack = [16][4]uint8{ + {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}, +} + +var ( + // The mapping from 4x4 region position to band is specified in section 13.3. + bands = [17]uint8{0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0} + // Category probabilties are specified in section 13.2. + // Decoding categories 1 and 2 are done inline. + cat3456 = [4][12]uint8{ + {173, 148, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {176, 155, 140, 135, 0, 0, 0, 0, 0, 0, 0, 0}, + {180, 157, 141, 134, 130, 0, 0, 0, 0, 0, 0, 0}, + {254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0}, + } + // The zigzag order is: + // 0 1 5 6 + // 2 4 7 12 + // 3 8 11 13 + // 9 10 14 15 + zigzag = [16]uint8{0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15} +) + +// parseResiduals4 parses a 4x4 region of residual coefficients, as specified +// in section 13.3, and returns a 0/1 value indicating whether there was at +// least one non-zero coefficient. +// r is the partition to read bits from. +// plane and context describe which token probability table to use. context is +// either 0, 1 or 2, and equals how many of the macroblock left and macroblock +// above have non-zero coefficients. +// quant are the DC/AC quantization factors. +// skipFirstCoeff is whether the DC coefficient has already been parsed. +// coeffBase is the base index of d.coeff to write to. +func (d *Decoder) parseResiduals4(r *partition, plane int, context uint8, quant [2]uint16, skipFirstCoeff bool, coeffBase int) uint8 { + prob, n := &d.tokenProb[plane], 0 + if skipFirstCoeff { + n = 1 + } + p := prob[bands[n]][context] + if !r.readBit(p[0]) { + return 0 + } + for n != 16 { + n++ + if !r.readBit(p[1]) { + p = prob[bands[n]][0] + continue + } + var v uint32 + if !r.readBit(p[2]) { + v = 1 + p = prob[bands[n]][1] + } else { + if !r.readBit(p[3]) { + if !r.readBit(p[4]) { + v = 2 + } else { + v = 3 + r.readUint(p[5], 1) + } + } else if !r.readBit(p[6]) { + if !r.readBit(p[7]) { + // Category 1. + v = 5 + r.readUint(159, 1) + } else { + // Category 2. + v = 7 + 2*r.readUint(165, 1) + r.readUint(145, 1) + } + } else { + // Categories 3, 4, 5 or 6. + b1 := r.readUint(p[8], 1) + b0 := r.readUint(p[9+b1], 1) + cat := 2*b1 + b0 + tab := &cat3456[cat] + v = 0 + for i := 0; tab[i] != 0; i++ { + v *= 2 + v += r.readUint(tab[i], 1) + } + v += 3 + (8 << cat) + } + p = prob[bands[n]][2] + } + z := zigzag[n-1] + c := int32(v) * int32(quant[btou(z > 0)]) + if r.readBit(uniformProb) { + c = -c + } + d.coeff[coeffBase+int(z)] = int16(c) + if n == 16 || !r.readBit(p[0]) { + return 1 + } + } + return 1 +} + +// parseResiduals parses the residuals and returns whether inner loop filtering +// should be skipped for this macroblock. +func (d *Decoder) parseResiduals(mbx, mby int) (skip bool) { + partition := &d.op[mby&(d.nOP-1)] + plane := planeY1SansY2 + quant := &d.quant[d.segment] + + // Parse the DC coefficient of each 4x4 luma region. + if d.usePredY16 { + nz := d.parseResiduals4(partition, planeY2, d.leftMB.nzY16+d.upMB[mbx].nzY16, quant.y2, false, whtCoeffBase) + d.leftMB.nzY16 = nz + d.upMB[mbx].nzY16 = nz + d.inverseWHT16() + plane = planeY1WithY2 + } + + var ( + nzDC, nzAC [4]uint8 + nzDCMask, nzACMask uint32 + coeffBase int + ) + + // Parse the luma coefficients. + lnz := unpack[d.leftMB.nzMask&0x0f] + unz := unpack[d.upMB[mbx].nzMask&0x0f] + for y := 0; y < 4; y++ { + nz := lnz[y] + for x := 0; x < 4; x++ { + nz = d.parseResiduals4(partition, plane, nz+unz[x], quant.y1, d.usePredY16, coeffBase) + unz[x] = nz + nzAC[x] = nz + nzDC[x] = btou(d.coeff[coeffBase] != 0) + coeffBase += 16 + } + lnz[y] = nz + nzDCMask |= pack(nzDC, y*4) + nzACMask |= pack(nzAC, y*4) + } + lnzMask := pack(lnz, 0) + unzMask := pack(unz, 0) + + // Parse the chroma coefficients. + lnz = unpack[d.leftMB.nzMask>>4] + unz = unpack[d.upMB[mbx].nzMask>>4] + for c := 0; c < 4; c += 2 { + for y := 0; y < 2; y++ { + nz := lnz[y+c] + for x := 0; x < 2; x++ { + nz = d.parseResiduals4(partition, planeUV, nz+unz[x+c], quant.uv, false, coeffBase) + unz[x+c] = nz + nzAC[y*2+x] = nz + nzDC[y*2+x] = btou(d.coeff[coeffBase] != 0) + coeffBase += 16 + } + lnz[y+c] = nz + } + nzDCMask |= pack(nzDC, 16+c*2) + nzACMask |= pack(nzAC, 16+c*2) + } + lnzMask |= pack(lnz, 4) + unzMask |= pack(unz, 4) + + // Save decoder state. + d.leftMB.nzMask = uint8(lnzMask) + d.upMB[mbx].nzMask = uint8(unzMask) + d.nzDCMask = nzDCMask + d.nzACMask = nzACMask + + // Section 15.1 of the spec says that "Steps 2 and 4 [of the loop filter] + // are skipped... [if] there is no DCT coefficient coded for the whole + // macroblock." + return nzDCMask == 0 && nzACMask == 0 +} + +// reconstructMacroblock applies the predictor functions and adds the inverse- +// DCT transformed residuals to recover the YCbCr data. +func (d *Decoder) reconstructMacroblock(mbx, mby int) { + if d.usePredY16 { + p := checkTopLeftPred(mbx, mby, d.predY16) + predFunc16[p](d, 1, 8) + for j := 0; j < 4; j++ { + for i := 0; i < 4; i++ { + n := 4*j + i + y := 4*j + 1 + x := 4*i + 8 + mask := uint32(1) << uint(n) + if d.nzACMask&mask != 0 { + d.inverseDCT4(y, x, 16*n) + } else if d.nzDCMask&mask != 0 { + d.inverseDCT4DCOnly(y, x, 16*n) + } + } + } + } else { + for j := 0; j < 4; j++ { + for i := 0; i < 4; i++ { + n := 4*j + i + y := 4*j + 1 + x := 4*i + 8 + predFunc4[d.predY4[j][i]](d, y, x) + mask := uint32(1) << uint(n) + if d.nzACMask&mask != 0 { + d.inverseDCT4(y, x, 16*n) + } else if d.nzDCMask&mask != 0 { + d.inverseDCT4DCOnly(y, x, 16*n) + } + } + } + } + p := checkTopLeftPred(mbx, mby, d.predC8) + predFunc8[p](d, ybrBY, ybrBX) + if d.nzACMask&0x0f0000 != 0 { + d.inverseDCT8(ybrBY, ybrBX, bCoeffBase) + } else if d.nzDCMask&0x0f0000 != 0 { + d.inverseDCT8DCOnly(ybrBY, ybrBX, bCoeffBase) + } + predFunc8[p](d, ybrRY, ybrRX) + if d.nzACMask&0xf00000 != 0 { + d.inverseDCT8(ybrRY, ybrRX, rCoeffBase) + } else if d.nzDCMask&0xf00000 != 0 { + d.inverseDCT8DCOnly(ybrRY, ybrRX, rCoeffBase) + } +} + +// reconstruct reconstructs one macroblock and returns whether inner loop +// filtering should be skipped for it. +func (d *Decoder) reconstruct(mbx, mby int) (skip bool) { + if d.segmentHeader.updateMap { + if !d.fp.readBit(d.segmentHeader.prob[0]) { + d.segment = int(d.fp.readUint(d.segmentHeader.prob[1], 1)) + } else { + d.segment = int(d.fp.readUint(d.segmentHeader.prob[2], 1)) + 2 + } + } + if d.useSkipProb { + skip = d.fp.readBit(d.skipProb) + } + // Prepare the workspace. + for i := range d.coeff { + d.coeff[i] = 0 + } + d.prepareYBR(mbx, mby) + // Parse the predictor modes. + d.usePredY16 = d.fp.readBit(145) + if d.usePredY16 { + d.parsePredModeY16(mbx) + } else { + d.parsePredModeY4(mbx) + } + d.parsePredModeC8() + // Parse the residuals. + if !skip { + skip = d.parseResiduals(mbx, mby) + } else { + if d.usePredY16 { + d.leftMB.nzY16 = 0 + d.upMB[mbx].nzY16 = 0 + } + d.leftMB.nzMask = 0 + d.upMB[mbx].nzMask = 0 + d.nzDCMask = 0 + d.nzACMask = 0 + } + // Reconstruct the YCbCr data and copy it to the image. + d.reconstructMacroblock(mbx, mby) + for i, y := (mby*d.img.YStride+mbx)*16, 0; y < 16; i, y = i+d.img.YStride, y+1 { + copy(d.img.Y[i:i+16], d.ybr[ybrYY+y][ybrYX:ybrYX+16]) + } + for i, y := (mby*d.img.CStride+mbx)*8, 0; y < 8; i, y = i+d.img.CStride, y+1 { + copy(d.img.Cb[i:i+8], d.ybr[ybrBY+y][ybrBX:ybrBX+8]) + copy(d.img.Cr[i:i+8], d.ybr[ybrRY+y][ybrRX:ybrRX+8]) + } + return skip +} diff --git a/vendor/golang.org/x/image/vp8/token.go b/vendor/golang.org/x/image/vp8/token.go @@ -0,0 +1,381 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8 + +// This file contains token probabilities for decoding DCT/WHT coefficients, as +// specified in chapter 13. + +func (d *Decoder) parseTokenProb() { + for i := range d.tokenProb { + for j := range d.tokenProb[i] { + for k := range d.tokenProb[i][j] { + for l := range d.tokenProb[i][j][k] { + if d.fp.readBit(tokenProbUpdateProb[i][j][k][l]) { + d.tokenProb[i][j][k][l] = uint8(d.fp.readUint(uniformProb, 8)) + } + } + } + } + } +} + +// The plane enumeration is specified in section 13.3. +const ( + planeY1WithY2 = iota + planeY2 + planeUV + planeY1SansY2 + nPlane +) + +const ( + nBand = 8 + nContext = 3 + nProb = 11 +) + +// Token probability update probabilities are specified in section 13.4. +var tokenProbUpdateProb = [nPlane][nBand][nContext][nProb]uint8{ + { + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255}, + {249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255}, + {234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255}, + {250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255}, + {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + }, + { + { + {217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255}, + {234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255}, + }, + { + {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + }, + { + { + {186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255}, + {234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255}, + {251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255}, + }, + { + {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + }, + { + { + {248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255}, + {248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255}, + {248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255}, + {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + }, +} + +// Default token probabilities are specified in section 13.5. +var defaultTokenProb = [nPlane][nBand][nContext][nProb]uint8{ + { + { + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + { + {253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128}, + {189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128}, + {106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128}, + }, + { + {1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128}, + {181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128}, + {78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128}, + }, + { + {1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128}, + {184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128}, + {77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128}, + }, + { + {1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128}, + {170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128}, + {37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128}, + }, + { + {1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128}, + {207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128}, + {102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128}, + }, + { + {1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128}, + {177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128}, + {80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128}, + }, + { + {1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + }, + { + { + {198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62}, + {131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1}, + {68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128}, + }, + { + {1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128}, + {184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128}, + {81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128}, + }, + { + {1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128}, + {99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128}, + {23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128}, + }, + { + {1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128}, + {109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128}, + {44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128}, + }, + { + {1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128}, + {94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128}, + {22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128}, + }, + { + {1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128}, + {124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128}, + {35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128}, + }, + { + {1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128}, + {121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128}, + {45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128}, + }, + { + {1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128}, + {203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128}, + {137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128}, + }, + }, + { + { + {253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128}, + {175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128}, + {73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128}, + }, + { + {1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128}, + {239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128}, + {155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128}, + }, + { + {1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128}, + {201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128}, + {69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128}, + }, + { + {1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128}, + {223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128}, + {141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128}, + }, + { + {1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128}, + {190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128}, + {149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + { + {1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + { + {1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128}, + {213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128}, + {55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + { + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + }, + { + { + {202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255}, + {126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128}, + {61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128}, + }, + { + {1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128}, + {166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128}, + {39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128}, + }, + { + {1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128}, + {124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128}, + {24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128}, + }, + { + {1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128}, + {149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128}, + {28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128}, + }, + { + {1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128}, + {123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128}, + {20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128}, + }, + { + {1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128}, + {168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128}, + {47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128}, + }, + { + {1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128}, + {141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128}, + {42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128}, + }, + { + {1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + {238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128}, + }, + }, +} diff --git a/vendor/golang.org/x/image/vp8l/decode.go b/vendor/golang.org/x/image/vp8l/decode.go @@ -0,0 +1,603 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vp8l implements a decoder for the VP8L lossless image format. +// +// The VP8L specification is at: +// https://developers.google.com/speed/webp/docs/riff_container +package vp8l // import "golang.org/x/image/vp8l" + +import ( + "bufio" + "errors" + "image" + "image/color" + "io" +) + +var ( + errInvalidCodeLengths = errors.New("vp8l: invalid code lengths") + errInvalidHuffmanTree = errors.New("vp8l: invalid Huffman tree") +) + +// colorCacheMultiplier is the multiplier used for the color cache hash +// function, specified in section 4.2.3. +const colorCacheMultiplier = 0x1e35a7bd + +// distanceMapTable is the look-up table for distanceMap. +var distanceMapTable = [120]uint8{ + 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, + 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, + 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, + 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, + 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, + 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, + 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, + 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, + 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, + 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, + 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, + 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70, +} + +// distanceMap maps a LZ77 backwards reference distance to a two-dimensional +// pixel offset, specified in section 4.2.2. +func distanceMap(w int32, code uint32) int32 { + if int32(code) > int32(len(distanceMapTable)) { + return int32(code) - int32(len(distanceMapTable)) + } + distCode := int32(distanceMapTable[code-1]) + yOffset := distCode >> 4 + xOffset := 8 - distCode&0xf + if d := yOffset*w + xOffset; d >= 1 { + return d + } + return 1 +} + +// decoder holds the bit-stream for a VP8L image. +type decoder struct { + r io.ByteReader + bits uint32 + nBits uint32 +} + +// read reads the next n bits from the decoder's bit-stream. +func (d *decoder) read(n uint32) (uint32, error) { + for d.nBits < n { + c, err := d.r.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return 0, err + } + d.bits |= uint32(c) << d.nBits + d.nBits += 8 + } + u := d.bits & (1<<n - 1) + d.bits >>= n + d.nBits -= n + return u, nil +} + +// decodeTransform decodes the next transform and the width of the image after +// transformation (or equivalently, before inverse transformation), specified +// in section 3. +func (d *decoder) decodeTransform(w int32, h int32) (t transform, newWidth int32, err error) { + t.oldWidth = w + t.transformType, err = d.read(2) + if err != nil { + return transform{}, 0, err + } + switch t.transformType { + case transformTypePredictor, transformTypeCrossColor: + t.bits, err = d.read(3) + if err != nil { + return transform{}, 0, err + } + t.bits += 2 + t.pix, err = d.decodePix(nTiles(w, t.bits), nTiles(h, t.bits), 0, false) + if err != nil { + return transform{}, 0, err + } + case transformTypeSubtractGreen: + // No-op. + case transformTypeColorIndexing: + nColors, err := d.read(8) + if err != nil { + return transform{}, 0, err + } + nColors++ + t.bits = 0 + switch { + case nColors <= 2: + t.bits = 3 + case nColors <= 4: + t.bits = 2 + case nColors <= 16: + t.bits = 1 + } + w = nTiles(w, t.bits) + pix, err := d.decodePix(int32(nColors), 1, 4*256, false) + if err != nil { + return transform{}, 0, err + } + for p := 4; p < len(pix); p += 4 { + pix[p+0] += pix[p-4] + pix[p+1] += pix[p-3] + pix[p+2] += pix[p-2] + pix[p+3] += pix[p-1] + } + // The spec says that "if the index is equal or larger than color_table_size, + // the argb color value should be set to 0x00000000 (transparent black)." + // We re-slice up to 256 4-byte pixels. + t.pix = pix[:4*256] + } + return t, w, nil +} + +// repeatsCodeLength is the minimum code length for repeated codes. +const repeatsCodeLength = 16 + +// These magic numbers are specified at the end of section 5.2.2. +// The 3-length arrays apply to code lengths >= repeatsCodeLength. +var ( + codeLengthCodeOrder = [19]uint8{ + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + } + repeatBits = [3]uint8{2, 3, 7} + repeatOffsets = [3]uint8{3, 3, 11} +) + +// decodeCodeLengths decodes a Huffman tree's code lengths which are themselves +// encoded via a Huffman tree, specified in section 5.2.2. +func (d *decoder) decodeCodeLengths(dst []uint32, codeLengthCodeLengths []uint32) error { + h := hTree{} + if err := h.build(codeLengthCodeLengths); err != nil { + return err + } + + maxSymbol := len(dst) + useLength, err := d.read(1) + if err != nil { + return err + } + if useLength != 0 { + n, err := d.read(3) + if err != nil { + return err + } + n = 2 + 2*n + ms, err := d.read(n) + if err != nil { + return err + } + maxSymbol = int(ms) + 2 + if maxSymbol > len(dst) { + return errInvalidCodeLengths + } + } + + // The spec says that "if code 16 [meaning repeat] is used before + // a non-zero value has been emitted, a value of 8 is repeated." + prevCodeLength := uint32(8) + + for symbol := 0; symbol < len(dst); { + if maxSymbol == 0 { + break + } + maxSymbol-- + codeLength, err := h.next(d) + if err != nil { + return err + } + if codeLength < repeatsCodeLength { + dst[symbol] = codeLength + symbol++ + if codeLength != 0 { + prevCodeLength = codeLength + } + continue + } + + repeat, err := d.read(uint32(repeatBits[codeLength-repeatsCodeLength])) + if err != nil { + return err + } + repeat += uint32(repeatOffsets[codeLength-repeatsCodeLength]) + if symbol+int(repeat) > len(dst) { + return errInvalidCodeLengths + } + // A code length of 16 repeats the previous non-zero code. + // A code length of 17 or 18 repeats zeroes. + cl := uint32(0) + if codeLength == 16 { + cl = prevCodeLength + } + for ; repeat > 0; repeat-- { + dst[symbol] = cl + symbol++ + } + } + return nil +} + +// decodeHuffmanTree decodes a Huffman tree into h. +func (d *decoder) decodeHuffmanTree(h *hTree, alphabetSize uint32) error { + useSimple, err := d.read(1) + if err != nil { + return err + } + if useSimple != 0 { + nSymbols, err := d.read(1) + if err != nil { + return err + } + nSymbols++ + firstSymbolLengthCode, err := d.read(1) + if err != nil { + return err + } + firstSymbolLengthCode = 7*firstSymbolLengthCode + 1 + var symbols [2]uint32 + symbols[0], err = d.read(firstSymbolLengthCode) + if err != nil { + return err + } + if nSymbols == 2 { + symbols[1], err = d.read(8) + if err != nil { + return err + } + } + return h.buildSimple(nSymbols, symbols, alphabetSize) + } + + nCodes, err := d.read(4) + if err != nil { + return err + } + nCodes += 4 + if int(nCodes) > len(codeLengthCodeOrder) { + return errInvalidHuffmanTree + } + codeLengthCodeLengths := [len(codeLengthCodeOrder)]uint32{} + for i := uint32(0); i < nCodes; i++ { + codeLengthCodeLengths[codeLengthCodeOrder[i]], err = d.read(3) + if err != nil { + return err + } + } + codeLengths := make([]uint32, alphabetSize) + if err = d.decodeCodeLengths(codeLengths, codeLengthCodeLengths[:]); err != nil { + return err + } + return h.build(codeLengths) +} + +const ( + huffGreen = 0 + huffRed = 1 + huffBlue = 2 + huffAlpha = 3 + huffDistance = 4 + nHuff = 5 +) + +// hGroup is an array of 5 Huffman trees. +type hGroup [nHuff]hTree + +// decodeHuffmanGroups decodes the one or more hGroups used to decode the pixel +// data. If one hGroup is used for the entire image, then hPix and hBits will +// be zero. If more than one hGroup is used, then hPix contains the meta-image +// that maps tiles to hGroup index, and hBits contains the log-2 tile size. +func (d *decoder) decodeHuffmanGroups(w int32, h int32, topLevel bool, ccBits uint32) ( + hGroups []hGroup, hPix []byte, hBits uint32, err error) { + + maxHGroupIndex := 0 + if topLevel { + useMeta, err := d.read(1) + if err != nil { + return nil, nil, 0, err + } + if useMeta != 0 { + hBits, err = d.read(3) + if err != nil { + return nil, nil, 0, err + } + hBits += 2 + hPix, err = d.decodePix(nTiles(w, hBits), nTiles(h, hBits), 0, false) + if err != nil { + return nil, nil, 0, err + } + for p := 0; p < len(hPix); p += 4 { + i := int(hPix[p])<<8 | int(hPix[p+1]) + if maxHGroupIndex < i { + maxHGroupIndex = i + } + } + } + } + hGroups = make([]hGroup, maxHGroupIndex+1) + for i := range hGroups { + for j, alphabetSize := range alphabetSizes { + if j == 0 && ccBits > 0 { + alphabetSize += 1 << ccBits + } + if err := d.decodeHuffmanTree(&hGroups[i][j], alphabetSize); err != nil { + return nil, nil, 0, err + } + } + } + return hGroups, hPix, hBits, nil +} + +const ( + nLiteralCodes = 256 + nLengthCodes = 24 + nDistanceCodes = 40 +) + +var alphabetSizes = [nHuff]uint32{ + nLiteralCodes + nLengthCodes, + nLiteralCodes, + nLiteralCodes, + nLiteralCodes, + nDistanceCodes, +} + +// decodePix decodes pixel data, specified in section 5.2.2. +func (d *decoder) decodePix(w int32, h int32, minCap int32, topLevel bool) ([]byte, error) { + // Decode the color cache parameters. + ccBits, ccShift, ccEntries := uint32(0), uint32(0), ([]uint32)(nil) + useColorCache, err := d.read(1) + if err != nil { + return nil, err + } + if useColorCache != 0 { + ccBits, err = d.read(4) + if err != nil { + return nil, err + } + if ccBits < 1 || 11 < ccBits { + return nil, errors.New("vp8l: invalid color cache parameters") + } + ccShift = 32 - ccBits + ccEntries = make([]uint32, 1<<ccBits) + } + + // Decode the Huffman groups. + hGroups, hPix, hBits, err := d.decodeHuffmanGroups(w, h, topLevel, ccBits) + if err != nil { + return nil, err + } + hMask, tilesPerRow := int32(0), int32(0) + if hBits != 0 { + hMask, tilesPerRow = 1<<hBits-1, nTiles(w, hBits) + } + + // Decode the pixels. + if minCap < 4*w*h { + minCap = 4 * w * h + } + pix := make([]byte, 4*w*h, minCap) + p, cachedP := 0, 0 + x, y := int32(0), int32(0) + hg, lookupHG := &hGroups[0], hMask != 0 + for p < len(pix) { + if lookupHG { + i := 4 * (tilesPerRow*(y>>hBits) + (x >> hBits)) + hg = &hGroups[uint32(hPix[i])<<8|uint32(hPix[i+1])] + } + + green, err := hg[huffGreen].next(d) + if err != nil { + return nil, err + } + switch { + case green < nLiteralCodes: + // We have a literal pixel. + red, err := hg[huffRed].next(d) + if err != nil { + return nil, err + } + blue, err := hg[huffBlue].next(d) + if err != nil { + return nil, err + } + alpha, err := hg[huffAlpha].next(d) + if err != nil { + return nil, err + } + pix[p+0] = uint8(red) + pix[p+1] = uint8(green) + pix[p+2] = uint8(blue) + pix[p+3] = uint8(alpha) + p += 4 + + x++ + if x == w { + x, y = 0, y+1 + } + lookupHG = hMask != 0 && x&hMask == 0 + + case green < nLiteralCodes+nLengthCodes: + // We have a LZ77 backwards reference. + length, err := d.lz77Param(green - nLiteralCodes) + if err != nil { + return nil, err + } + distSym, err := hg[huffDistance].next(d) + if err != nil { + return nil, err + } + distCode, err := d.lz77Param(distSym) + if err != nil { + return nil, err + } + dist := distanceMap(w, distCode) + pEnd := p + 4*int(length) + q := p - 4*int(dist) + qEnd := pEnd - 4*int(dist) + if p < 0 || len(pix) < pEnd || q < 0 || len(pix) < qEnd { + return nil, errors.New("vp8l: invalid LZ77 parameters") + } + for ; p < pEnd; p, q = p+1, q+1 { + pix[p] = pix[q] + } + + x += int32(length) + for x >= w { + x, y = x-w, y+1 + } + lookupHG = hMask != 0 + + default: + // We have a color cache lookup. First, insert previous pixels + // into the cache. Note that VP8L assumes ARGB order, but the + // Go image.RGBA type is in RGBA order. + for ; cachedP < p; cachedP += 4 { + argb := uint32(pix[cachedP+0])<<16 | + uint32(pix[cachedP+1])<<8 | + uint32(pix[cachedP+2])<<0 | + uint32(pix[cachedP+3])<<24 + ccEntries[(argb*colorCacheMultiplier)>>ccShift] = argb + } + green -= nLiteralCodes + nLengthCodes + if int(green) >= len(ccEntries) { + return nil, errors.New("vp8l: invalid color cache index") + } + argb := ccEntries[green] + pix[p+0] = uint8(argb >> 16) + pix[p+1] = uint8(argb >> 8) + pix[p+2] = uint8(argb >> 0) + pix[p+3] = uint8(argb >> 24) + p += 4 + + x++ + if x == w { + x, y = 0, y+1 + } + lookupHG = hMask != 0 && x&hMask == 0 + } + } + return pix, nil +} + +// lz77Param returns the next LZ77 parameter: a length or a distance, specified +// in section 4.2.2. +func (d *decoder) lz77Param(symbol uint32) (uint32, error) { + if symbol < 4 { + return symbol + 1, nil + } + extraBits := (symbol - 2) >> 1 + offset := (2 + symbol&1) << extraBits + n, err := d.read(extraBits) + if err != nil { + return 0, err + } + return offset + n + 1, nil +} + +// decodeHeader decodes the VP8L header from r. +func decodeHeader(r io.Reader) (d *decoder, w int32, h int32, err error) { + rr, ok := r.(io.ByteReader) + if !ok { + rr = bufio.NewReader(r) + } + d = &decoder{r: rr} + magic, err := d.read(8) + if err != nil { + return nil, 0, 0, err + } + if magic != 0x2f { + return nil, 0, 0, errors.New("vp8l: invalid header") + } + width, err := d.read(14) + if err != nil { + return nil, 0, 0, err + } + width++ + height, err := d.read(14) + if err != nil { + return nil, 0, 0, err + } + height++ + _, err = d.read(1) // Read and ignore the hasAlpha hint. + if err != nil { + return nil, 0, 0, err + } + version, err := d.read(3) + if err != nil { + return nil, 0, 0, err + } + if version != 0 { + return nil, 0, 0, errors.New("vp8l: invalid version") + } + return d, int32(width), int32(height), nil +} + +// DecodeConfig decodes the color model and dimensions of a VP8L image from r. +func DecodeConfig(r io.Reader) (image.Config, error) { + _, w, h, err := decodeHeader(r) + if err != nil { + return image.Config{}, err + } + return image.Config{ + ColorModel: color.NRGBAModel, + Width: int(w), + Height: int(h), + }, nil +} + +// Decode decodes a VP8L image from r. +func Decode(r io.Reader) (image.Image, error) { + d, w, h, err := decodeHeader(r) + if err != nil { + return nil, err + } + // Decode the transforms. + var ( + nTransforms int + transforms [nTransformTypes]transform + transformsSeen [nTransformTypes]bool + originalW = w + ) + for { + more, err := d.read(1) + if err != nil { + return nil, err + } + if more == 0 { + break + } + var t transform + t, w, err = d.decodeTransform(w, h) + if err != nil { + return nil, err + } + if transformsSeen[t.transformType] { + return nil, errors.New("vp8l: repeated transform") + } + transformsSeen[t.transformType] = true + transforms[nTransforms] = t + nTransforms++ + } + // Decode the transformed pixels. + pix, err := d.decodePix(w, h, 0, true) + if err != nil { + return nil, err + } + // Apply the inverse transformations. + for i := nTransforms - 1; i >= 0; i-- { + t := &transforms[i] + pix = inverseTransforms[t.transformType](t, pix, h) + } + return &image.NRGBA{ + Pix: pix, + Stride: 4 * int(originalW), + Rect: image.Rect(0, 0, int(originalW), int(h)), + }, nil +} diff --git a/vendor/golang.org/x/image/vp8l/huffman.go b/vendor/golang.org/x/image/vp8l/huffman.go @@ -0,0 +1,245 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8l + +import ( + "io" +) + +// reverseBits reverses the bits in a byte. +var reverseBits = [256]uint8{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +} + +// hNode is a node in a Huffman tree. +type hNode struct { + // symbol is the symbol held by this node. + symbol uint32 + // children, if positive, is the hTree.nodes index of the first of + // this node's two children. Zero means an uninitialized node, + // and -1 means a leaf node. + children int32 +} + +const leafNode = -1 + +// lutSize is the log-2 size of an hTree's look-up table. +const lutSize, lutMask = 7, 1<<7 - 1 + +// hTree is a Huffman tree. +type hTree struct { + // nodes are the nodes of the Huffman tree. During construction, + // len(nodes) grows from 1 up to cap(nodes) by steps of two. + // After construction, len(nodes) == cap(nodes), and both equal + // 2*theNumberOfSymbols - 1. + nodes []hNode + // lut is a look-up table for walking the nodes. The x in lut[x] is + // the next lutSize bits in the bit-stream. The low 8 bits of lut[x] + // equals 1 plus the number of bits in the next code, or 0 if the + // next code requires more than lutSize bits. The high 24 bits are: + // - the symbol, if the code requires lutSize or fewer bits, or + // - the hTree.nodes index to start the tree traversal from, if + // the next code requires more than lutSize bits. + lut [1 << lutSize]uint32 +} + +// insert inserts into the hTree a symbol whose encoding is the least +// significant codeLength bits of code. +func (h *hTree) insert(symbol uint32, code uint32, codeLength uint32) error { + if symbol > 0xffff || codeLength > 0xfe { + return errInvalidHuffmanTree + } + baseCode := uint32(0) + if codeLength > lutSize { + baseCode = uint32(reverseBits[(code>>(codeLength-lutSize))&0xff]) >> (8 - lutSize) + } else { + baseCode = uint32(reverseBits[code&0xff]) >> (8 - codeLength) + for i := 0; i < 1<<(lutSize-codeLength); i++ { + h.lut[baseCode|uint32(i)<<codeLength] = symbol<<8 | (codeLength + 1) + } + } + + n := uint32(0) + for jump := lutSize; codeLength > 0; { + codeLength-- + if int(n) > len(h.nodes) { + return errInvalidHuffmanTree + } + switch h.nodes[n].children { + case leafNode: + return errInvalidHuffmanTree + case 0: + if len(h.nodes) == cap(h.nodes) { + return errInvalidHuffmanTree + } + // Create two empty child nodes. + h.nodes[n].children = int32(len(h.nodes)) + h.nodes = h.nodes[:len(h.nodes)+2] + } + n = uint32(h.nodes[n].children) + 1&(code>>codeLength) + jump-- + if jump == 0 && h.lut[baseCode] == 0 { + h.lut[baseCode] = n << 8 + } + } + + switch h.nodes[n].children { + case leafNode: + // No-op. + case 0: + // Turn the uninitialized node into a leaf. + h.nodes[n].children = leafNode + default: + return errInvalidHuffmanTree + } + h.nodes[n].symbol = symbol + return nil +} + +// codeLengthsToCodes returns the canonical Huffman codes implied by the +// sequence of code lengths. +func codeLengthsToCodes(codeLengths []uint32) ([]uint32, error) { + maxCodeLength := uint32(0) + for _, cl := range codeLengths { + if maxCodeLength < cl { + maxCodeLength = cl + } + } + const maxAllowedCodeLength = 15 + if len(codeLengths) == 0 || maxCodeLength > maxAllowedCodeLength { + return nil, errInvalidHuffmanTree + } + histogram := [maxAllowedCodeLength + 1]uint32{} + for _, cl := range codeLengths { + histogram[cl]++ + } + currCode, nextCodes := uint32(0), [maxAllowedCodeLength + 1]uint32{} + for cl := 1; cl < len(nextCodes); cl++ { + currCode = (currCode + histogram[cl-1]) << 1 + nextCodes[cl] = currCode + } + codes := make([]uint32, len(codeLengths)) + for symbol, cl := range codeLengths { + if cl > 0 { + codes[symbol] = nextCodes[cl] + nextCodes[cl]++ + } + } + return codes, nil +} + +// build builds a canonical Huffman tree from the given code lengths. +func (h *hTree) build(codeLengths []uint32) error { + // Calculate the number of symbols. + var nSymbols, lastSymbol uint32 + for symbol, cl := range codeLengths { + if cl != 0 { + nSymbols++ + lastSymbol = uint32(symbol) + } + } + if nSymbols == 0 { + return errInvalidHuffmanTree + } + h.nodes = make([]hNode, 1, 2*nSymbols-1) + // Handle the trivial case. + if nSymbols == 1 { + if len(codeLengths) <= int(lastSymbol) { + return errInvalidHuffmanTree + } + return h.insert(lastSymbol, 0, 0) + } + // Handle the non-trivial case. + codes, err := codeLengthsToCodes(codeLengths) + if err != nil { + return err + } + for symbol, cl := range codeLengths { + if cl > 0 { + if err := h.insert(uint32(symbol), codes[symbol], cl); err != nil { + return err + } + } + } + return nil +} + +// buildSimple builds a Huffman tree with 1 or 2 symbols. +func (h *hTree) buildSimple(nSymbols uint32, symbols [2]uint32, alphabetSize uint32) error { + h.nodes = make([]hNode, 1, 2*nSymbols-1) + for i := uint32(0); i < nSymbols; i++ { + if symbols[i] >= alphabetSize { + return errInvalidHuffmanTree + } + if err := h.insert(symbols[i], i, nSymbols-1); err != nil { + return err + } + } + return nil +} + +// next returns the next Huffman-encoded symbol from the bit-stream d. +func (h *hTree) next(d *decoder) (uint32, error) { + var n uint32 + // Read enough bits so that we can use the look-up table. + if d.nBits < lutSize { + c, err := d.r.ReadByte() + if err != nil { + if err == io.EOF { + // There are no more bytes of data, but we may still be able + // to read the next symbol out of the previously read bits. + goto slowPath + } + return 0, err + } + d.bits |= uint32(c) << d.nBits + d.nBits += 8 + } + // Use the look-up table. + n = h.lut[d.bits&lutMask] + if b := n & 0xff; b != 0 { + b-- + d.bits >>= b + d.nBits -= b + return n >> 8, nil + } + n >>= 8 + d.bits >>= lutSize + d.nBits -= lutSize + +slowPath: + for h.nodes[n].children != leafNode { + if d.nBits == 0 { + c, err := d.r.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return 0, err + } + d.bits = uint32(c) + d.nBits = 8 + } + n = uint32(h.nodes[n].children) + 1&d.bits + d.bits >>= 1 + d.nBits-- + } + return h.nodes[n].symbol, nil +} diff --git a/vendor/golang.org/x/image/vp8l/transform.go b/vendor/golang.org/x/image/vp8l/transform.go @@ -0,0 +1,299 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vp8l + +// This file deals with image transforms, specified in section 3. + +// nTiles returns the number of tiles needed to cover size pixels, where each +// tile's side is 1<<bits pixels long. +func nTiles(size int32, bits uint32) int32 { + return (size + 1<<bits - 1) >> bits +} + +const ( + transformTypePredictor = 0 + transformTypeCrossColor = 1 + transformTypeSubtractGreen = 2 + transformTypeColorIndexing = 3 + nTransformTypes = 4 +) + +// transform holds the parameters for an invertible transform. +type transform struct { + // transformType is the type of the transform. + transformType uint32 + // oldWidth is the width of the image before transformation (or + // equivalently, after inverse transformation). The color-indexing + // transform can reduce the width. For example, a 50-pixel-wide + // image that only needs 4 bits (half a byte) per color index can + // be transformed into a 25-pixel-wide image. + oldWidth int32 + // bits is the log-2 size of the transform's tiles, for the predictor + // and cross-color transforms. 8>>bits is the number of bits per + // color index, for the color-index transform. + bits uint32 + // pix is the tile values, for the predictor and cross-color + // transforms, and the color palette, for the color-index transform. + pix []byte +} + +var inverseTransforms = [nTransformTypes]func(*transform, []byte, int32) []byte{ + transformTypePredictor: inversePredictor, + transformTypeCrossColor: inverseCrossColor, + transformTypeSubtractGreen: inverseSubtractGreen, + transformTypeColorIndexing: inverseColorIndexing, +} + +func inversePredictor(t *transform, pix []byte, h int32) []byte { + if t.oldWidth == 0 || h == 0 { + return pix + } + // The first pixel's predictor is mode 0 (opaque black). + pix[3] += 0xff + p, mask := int32(4), int32(1)<<t.bits-1 + for x := int32(1); x < t.oldWidth; x++ { + // The rest of the first row's predictor is mode 1 (L). + pix[p+0] += pix[p-4] + pix[p+1] += pix[p-3] + pix[p+2] += pix[p-2] + pix[p+3] += pix[p-1] + p += 4 + } + top, tilesPerRow := 0, nTiles(t.oldWidth, t.bits) + for y := int32(1); y < h; y++ { + // The first column's predictor is mode 2 (T). + pix[p+0] += pix[top+0] + pix[p+1] += pix[top+1] + pix[p+2] += pix[top+2] + pix[p+3] += pix[top+3] + p, top = p+4, top+4 + + q := 4 * (y >> t.bits) * tilesPerRow + predictorMode := t.pix[q+1] & 0x0f + q += 4 + for x := int32(1); x < t.oldWidth; x++ { + if x&mask == 0 { + predictorMode = t.pix[q+1] & 0x0f + q += 4 + } + switch predictorMode { + case 0: // Opaque black. + pix[p+3] += 0xff + + case 1: // L. + pix[p+0] += pix[p-4] + pix[p+1] += pix[p-3] + pix[p+2] += pix[p-2] + pix[p+3] += pix[p-1] + + case 2: // T. + pix[p+0] += pix[top+0] + pix[p+1] += pix[top+1] + pix[p+2] += pix[top+2] + pix[p+3] += pix[top+3] + + case 3: // TR. + pix[p+0] += pix[top+4] + pix[p+1] += pix[top+5] + pix[p+2] += pix[top+6] + pix[p+3] += pix[top+7] + + case 4: // TL. + pix[p+0] += pix[top-4] + pix[p+1] += pix[top-3] + pix[p+2] += pix[top-2] + pix[p+3] += pix[top-1] + + case 5: // Average2(Average2(L, TR), T). + pix[p+0] += avg2(avg2(pix[p-4], pix[top+4]), pix[top+0]) + pix[p+1] += avg2(avg2(pix[p-3], pix[top+5]), pix[top+1]) + pix[p+2] += avg2(avg2(pix[p-2], pix[top+6]), pix[top+2]) + pix[p+3] += avg2(avg2(pix[p-1], pix[top+7]), pix[top+3]) + + case 6: // Average2(L, TL). + pix[p+0] += avg2(pix[p-4], pix[top-4]) + pix[p+1] += avg2(pix[p-3], pix[top-3]) + pix[p+2] += avg2(pix[p-2], pix[top-2]) + pix[p+3] += avg2(pix[p-1], pix[top-1]) + + case 7: // Average2(L, T). + pix[p+0] += avg2(pix[p-4], pix[top+0]) + pix[p+1] += avg2(pix[p-3], pix[top+1]) + pix[p+2] += avg2(pix[p-2], pix[top+2]) + pix[p+3] += avg2(pix[p-1], pix[top+3]) + + case 8: // Average2(TL, T). + pix[p+0] += avg2(pix[top-4], pix[top+0]) + pix[p+1] += avg2(pix[top-3], pix[top+1]) + pix[p+2] += avg2(pix[top-2], pix[top+2]) + pix[p+3] += avg2(pix[top-1], pix[top+3]) + + case 9: // Average2(T, TR). + pix[p+0] += avg2(pix[top+0], pix[top+4]) + pix[p+1] += avg2(pix[top+1], pix[top+5]) + pix[p+2] += avg2(pix[top+2], pix[top+6]) + pix[p+3] += avg2(pix[top+3], pix[top+7]) + + case 10: // Average2(Average2(L, TL), Average2(T, TR)). + pix[p+0] += avg2(avg2(pix[p-4], pix[top-4]), avg2(pix[top+0], pix[top+4])) + pix[p+1] += avg2(avg2(pix[p-3], pix[top-3]), avg2(pix[top+1], pix[top+5])) + pix[p+2] += avg2(avg2(pix[p-2], pix[top-2]), avg2(pix[top+2], pix[top+6])) + pix[p+3] += avg2(avg2(pix[p-1], pix[top-1]), avg2(pix[top+3], pix[top+7])) + + case 11: // Select(L, T, TL). + l0 := int32(pix[p-4]) + l1 := int32(pix[p-3]) + l2 := int32(pix[p-2]) + l3 := int32(pix[p-1]) + c0 := int32(pix[top-4]) + c1 := int32(pix[top-3]) + c2 := int32(pix[top-2]) + c3 := int32(pix[top-1]) + t0 := int32(pix[top+0]) + t1 := int32(pix[top+1]) + t2 := int32(pix[top+2]) + t3 := int32(pix[top+3]) + l := abs(c0-t0) + abs(c1-t1) + abs(c2-t2) + abs(c3-t3) + t := abs(c0-l0) + abs(c1-l1) + abs(c2-l2) + abs(c3-l3) + if l < t { + pix[p+0] += uint8(l0) + pix[p+1] += uint8(l1) + pix[p+2] += uint8(l2) + pix[p+3] += uint8(l3) + } else { + pix[p+0] += uint8(t0) + pix[p+1] += uint8(t1) + pix[p+2] += uint8(t2) + pix[p+3] += uint8(t3) + } + + case 12: // ClampAddSubtractFull(L, T, TL). + pix[p+0] += clampAddSubtractFull(pix[p-4], pix[top+0], pix[top-4]) + pix[p+1] += clampAddSubtractFull(pix[p-3], pix[top+1], pix[top-3]) + pix[p+2] += clampAddSubtractFull(pix[p-2], pix[top+2], pix[top-2]) + pix[p+3] += clampAddSubtractFull(pix[p-1], pix[top+3], pix[top-1]) + + case 13: // ClampAddSubtractHalf(Average2(L, T), TL). + pix[p+0] += clampAddSubtractHalf(avg2(pix[p-4], pix[top+0]), pix[top-4]) + pix[p+1] += clampAddSubtractHalf(avg2(pix[p-3], pix[top+1]), pix[top-3]) + pix[p+2] += clampAddSubtractHalf(avg2(pix[p-2], pix[top+2]), pix[top-2]) + pix[p+3] += clampAddSubtractHalf(avg2(pix[p-1], pix[top+3]), pix[top-1]) + } + p, top = p+4, top+4 + } + } + return pix +} + +func inverseCrossColor(t *transform, pix []byte, h int32) []byte { + var greenToRed, greenToBlue, redToBlue int32 + p, mask, tilesPerRow := int32(0), int32(1)<<t.bits-1, nTiles(t.oldWidth, t.bits) + for y := int32(0); y < h; y++ { + q := 4 * (y >> t.bits) * tilesPerRow + for x := int32(0); x < t.oldWidth; x++ { + if x&mask == 0 { + redToBlue = int32(int8(t.pix[q+0])) + greenToBlue = int32(int8(t.pix[q+1])) + greenToRed = int32(int8(t.pix[q+2])) + q += 4 + } + red := pix[p+0] + green := pix[p+1] + blue := pix[p+2] + red += uint8(uint32(greenToRed*int32(int8(green))) >> 5) + blue += uint8(uint32(greenToBlue*int32(int8(green))) >> 5) + blue += uint8(uint32(redToBlue*int32(int8(red))) >> 5) + pix[p+0] = red + pix[p+2] = blue + p += 4 + } + } + return pix +} + +func inverseSubtractGreen(t *transform, pix []byte, h int32) []byte { + for p := 0; p < len(pix); p += 4 { + green := pix[p+1] + pix[p+0] += green + pix[p+2] += green + } + return pix +} + +func inverseColorIndexing(t *transform, pix []byte, h int32) []byte { + if t.bits == 0 { + for p := 0; p < len(pix); p += 4 { + i := 4 * uint32(pix[p+1]) + pix[p+0] = t.pix[i+0] + pix[p+1] = t.pix[i+1] + pix[p+2] = t.pix[i+2] + pix[p+3] = t.pix[i+3] + } + return pix + } + + vMask, xMask, bitsPerPixel := uint32(0), int32(0), uint32(8>>t.bits) + switch t.bits { + case 1: + vMask, xMask = 0x0f, 0x01 + case 2: + vMask, xMask = 0x03, 0x03 + case 3: + vMask, xMask = 0x01, 0x07 + } + + d, p, v, dst := 0, 0, uint32(0), make([]byte, 4*t.oldWidth*h) + for y := int32(0); y < h; y++ { + for x := int32(0); x < t.oldWidth; x++ { + if x&xMask == 0 { + v = uint32(pix[p+1]) + p += 4 + } + + i := 4 * (v & vMask) + dst[d+0] = t.pix[i+0] + dst[d+1] = t.pix[i+1] + dst[d+2] = t.pix[i+2] + dst[d+3] = t.pix[i+3] + d += 4 + + v >>= bitsPerPixel + } + } + return dst +} + +func abs(x int32) int32 { + if x < 0 { + return -x + } + return x +} + +func avg2(a, b uint8) uint8 { + return uint8((int32(a) + int32(b)) / 2) +} + +func clampAddSubtractFull(a, b, c uint8) uint8 { + x := int32(a) + int32(b) - int32(c) + if x < 0 { + return 0 + } + if x > 255 { + return 255 + } + return uint8(x) +} + +func clampAddSubtractHalf(a, b uint8) uint8 { + x := int32(a) + (int32(a)-int32(b))/2 + if x < 0 { + return 0 + } + if x > 255 { + return 255 + } + return uint8(x) +} diff --git a/vendor/golang.org/x/image/webp/decode.go b/vendor/golang.org/x/image/webp/decode.go @@ -0,0 +1,271 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package webp + +import ( + "bytes" + "errors" + "image" + "image/color" + "io" + + "golang.org/x/image/riff" + "golang.org/x/image/vp8" + "golang.org/x/image/vp8l" +) + +var errInvalidFormat = errors.New("webp: invalid format") + +var ( + fccALPH = riff.FourCC{'A', 'L', 'P', 'H'} + fccVP8 = riff.FourCC{'V', 'P', '8', ' '} + fccVP8L = riff.FourCC{'V', 'P', '8', 'L'} + fccVP8X = riff.FourCC{'V', 'P', '8', 'X'} + fccWEBP = riff.FourCC{'W', 'E', 'B', 'P'} +) + +func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) { + formType, riffReader, err := riff.NewReader(r) + if err != nil { + return nil, image.Config{}, err + } + if formType != fccWEBP { + return nil, image.Config{}, errInvalidFormat + } + + var ( + alpha []byte + alphaStride int + wantAlpha bool + widthMinusOne uint32 + heightMinusOne uint32 + buf [10]byte + ) + for { + chunkID, chunkLen, chunkData, err := riffReader.Next() + if err == io.EOF { + err = errInvalidFormat + } + if err != nil { + return nil, image.Config{}, err + } + + switch chunkID { + case fccALPH: + if !wantAlpha { + return nil, image.Config{}, errInvalidFormat + } + wantAlpha = false + // Read the Pre-processing | Filter | Compression byte. + if _, err := io.ReadFull(chunkData, buf[:1]); err != nil { + if err == io.EOF { + err = errInvalidFormat + } + return nil, image.Config{}, err + } + alpha, alphaStride, err = readAlpha(chunkData, widthMinusOne, heightMinusOne, buf[0]&0x03) + if err != nil { + return nil, image.Config{}, err + } + unfilterAlpha(alpha, alphaStride, (buf[0]>>2)&0x03) + + case fccVP8: + if wantAlpha || int32(chunkLen) < 0 { + return nil, image.Config{}, errInvalidFormat + } + d := vp8.NewDecoder() + d.Init(chunkData, int(chunkLen)) + fh, err := d.DecodeFrameHeader() + if err != nil { + return nil, image.Config{}, err + } + if configOnly { + return nil, image.Config{ + ColorModel: color.YCbCrModel, + Width: fh.Width, + Height: fh.Height, + }, nil + } + m, err := d.DecodeFrame() + if err != nil { + return nil, image.Config{}, err + } + if alpha != nil { + return &image.NYCbCrA{ + YCbCr: *m, + A: alpha, + AStride: alphaStride, + }, image.Config{}, nil + } + return m, image.Config{}, nil + + case fccVP8L: + if wantAlpha || alpha != nil { + return nil, image.Config{}, errInvalidFormat + } + if configOnly { + c, err := vp8l.DecodeConfig(chunkData) + return nil, c, err + } + m, err := vp8l.Decode(chunkData) + return m, image.Config{}, err + + case fccVP8X: + if chunkLen != 10 { + return nil, image.Config{}, errInvalidFormat + } + if _, err := io.ReadFull(chunkData, buf[:10]); err != nil { + return nil, image.Config{}, err + } + const ( + animationBit = 1 << 1 + xmpMetadataBit = 1 << 2 + exifMetadataBit = 1 << 3 + alphaBit = 1 << 4 + iccProfileBit = 1 << 5 + ) + wantAlpha = (buf[0] & alphaBit) != 0 + widthMinusOne = uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16 + heightMinusOne = uint32(buf[7]) | uint32(buf[8])<<8 | uint32(buf[9])<<16 + if configOnly { + if wantAlpha { + return nil, image.Config{ + ColorModel: color.NYCbCrAModel, + Width: int(widthMinusOne) + 1, + Height: int(heightMinusOne) + 1, + }, nil + } + return nil, image.Config{ + ColorModel: color.YCbCrModel, + Width: int(widthMinusOne) + 1, + Height: int(heightMinusOne) + 1, + }, nil + } + } + } +} + +func readAlpha(chunkData io.Reader, widthMinusOne, heightMinusOne uint32, compression byte) ( + alpha []byte, alphaStride int, err error) { + + switch compression { + case 0: + w := int(widthMinusOne) + 1 + h := int(heightMinusOne) + 1 + alpha = make([]byte, w*h) + if _, err := io.ReadFull(chunkData, alpha); err != nil { + return nil, 0, err + } + return alpha, w, nil + + case 1: + // Read the VP8L-compressed alpha values. First, synthesize a 5-byte VP8L header: + // a 1-byte magic number, a 14-bit widthMinusOne, a 14-bit heightMinusOne, + // a 1-bit (ignored, zero) alphaIsUsed and a 3-bit (zero) version. + // TODO(nigeltao): be more efficient than decoding an *image.NRGBA just to + // extract the green values to a separately allocated []byte. Fixing this + // will require changes to the vp8l package's API. + if widthMinusOne > 0x3fff || heightMinusOne > 0x3fff { + return nil, 0, errors.New("webp: invalid format") + } + alphaImage, err := vp8l.Decode(io.MultiReader( + bytes.NewReader([]byte{ + 0x2f, // VP8L magic number. + uint8(widthMinusOne), + uint8(widthMinusOne>>8) | uint8(heightMinusOne<<6), + uint8(heightMinusOne >> 2), + uint8(heightMinusOne >> 10), + }), + chunkData, + )) + if err != nil { + return nil, 0, err + } + // The green values of the inner NRGBA image are the alpha values of the + // outer NYCbCrA image. + pix := alphaImage.(*image.NRGBA).Pix + alpha = make([]byte, len(pix)/4) + for i := range alpha { + alpha[i] = pix[4*i+1] + } + return alpha, int(widthMinusOne) + 1, nil + } + return nil, 0, errInvalidFormat +} + +func unfilterAlpha(alpha []byte, alphaStride int, filter byte) { + if len(alpha) == 0 || alphaStride == 0 { + return + } + switch filter { + case 1: // Horizontal filter. + for i := 1; i < alphaStride; i++ { + alpha[i] += alpha[i-1] + } + for i := alphaStride; i < len(alpha); i += alphaStride { + // The first column is equivalent to the vertical filter. + alpha[i] += alpha[i-alphaStride] + + for j := 1; j < alphaStride; j++ { + alpha[i+j] += alpha[i+j-1] + } + } + + case 2: // Vertical filter. + // The first row is equivalent to the horizontal filter. + for i := 1; i < alphaStride; i++ { + alpha[i] += alpha[i-1] + } + + for i := alphaStride; i < len(alpha); i++ { + alpha[i] += alpha[i-alphaStride] + } + + case 3: // Gradient filter. + // The first row is equivalent to the horizontal filter. + for i := 1; i < alphaStride; i++ { + alpha[i] += alpha[i-1] + } + + for i := alphaStride; i < len(alpha); i += alphaStride { + // The first column is equivalent to the vertical filter. + alpha[i] += alpha[i-alphaStride] + + // The interior is predicted on the three top/left pixels. + for j := 1; j < alphaStride; j++ { + c := int(alpha[i+j-alphaStride-1]) + b := int(alpha[i+j-alphaStride]) + a := int(alpha[i+j-1]) + x := a + b - c + if x < 0 { + x = 0 + } else if x > 255 { + x = 255 + } + alpha[i+j] += uint8(x) + } + } + } +} + +// Decode reads a WEBP image from r and returns it as an image.Image. +func Decode(r io.Reader) (image.Image, error) { + m, _, err := decode(r, false) + if err != nil { + return nil, err + } + return m, err +} + +// DecodeConfig returns the color model and dimensions of a WEBP image without +// decoding the entire image. +func DecodeConfig(r io.Reader) (image.Config, error) { + _, c, err := decode(r, true) + return c, err +} + +func init() { + image.RegisterFormat("webp", "RIFF????WEBPVP8", Decode, DecodeConfig) +} diff --git a/vendor/golang.org/x/image/webp/doc.go b/vendor/golang.org/x/image/webp/doc.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package webp implements a decoder for WEBP images. +// +// WEBP is defined at: +// https://developers.google.com/speed/webp/docs/riff_container +package webp // import "golang.org/x/image/webp" diff --git a/vendor/modules.txt b/vendor/modules.txt @@ -569,7 +569,7 @@ github.com/superseriousbusiness/activity/streams/values/rfc2045 github.com/superseriousbusiness/activity/streams/values/rfc5988 github.com/superseriousbusiness/activity/streams/values/string github.com/superseriousbusiness/activity/streams/vocab -# github.com/superseriousbusiness/exif-terminator v0.4.0 +# github.com/superseriousbusiness/exif-terminator v0.5.0 ## explicit; go 1.17 github.com/superseriousbusiness/exif-terminator # github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe @@ -660,12 +660,16 @@ golang.org/x/crypto/ssh/internal/bcrypt_pbkdf ## explicit; go 1.18 golang.org/x/exp/constraints golang.org/x/exp/slices -# golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 +# golang.org/x/image v0.1.0 ## explicit; go 1.12 golang.org/x/image/bmp golang.org/x/image/ccitt +golang.org/x/image/riff golang.org/x/image/tiff golang.org/x/image/tiff/lzw +golang.org/x/image/vp8 +golang.org/x/image/vp8l +golang.org/x/image/webp # golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 ## explicit; go 1.17 golang.org/x/mod/semver