[
  {
    "path": ".gitignore",
    "content": "/.idea\n/vendor\n.DS_Store\n/example/output*\n"
  },
  {
    "path": ".travis.yml",
    "content": "dist: bionic\nlanguage: go\ngo:\n  - 1.13.x\n  - 1.14.x\n  - 1.15.x\n  - 1.16.x\n  - 1.x\n\nscript:\n  - go vet ./...\n  - go test -race -coverprofile=coverage.txt -covermode=atomic ./...\n\nbefore_install:\n  - sudo apt-get install libwebp-dev\n  - go mod vendor -v\n\nafter_success:\n  - bash <(curl -s https://codecov.io/bash)\n\naddons:\n  apt:\n    update: true\n"
  },
  {
    "path": "AUTHORS",
    "content": "Google LLC (https://opensource.google.com/)\nAmangeldy Kadyl <lex0.kz@gmail.com>\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2019 Amangeldy Kadyl\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: vendor test\n\nGO ?= $(which go)\n\ntest:\n\t@GO test ./... -v\n\nvendor:\n\t@GO mod vendor\n\nvet:\n\t@GO vet ./... -v\n\nbench:\n\t@GO test -bench=. ./webp\n\nlibwebp-mac:\n\tbrew install webp\n\nlibwebp-ubuntu:\n\tsudo apt-get install libwebp-dev\n"
  },
  {
    "path": "README.md",
    "content": "![logo](test_data/images/logo.jpg)\n# go-webp\n[![Build Status](https://api.travis-ci.com/kolesa-team/go-webp.svg?branch=master)](https://travis-ci.com/github/kolesa-team/go-webp)\n[![GoDoc](https://godoc.org/github.com/kolesa-team/go-webp?status.svg)](https://godoc.org/github.com/kolesa-team/go-webp)\n[![Go Report](https://goreportcard.com/badge/github.com/kolesa-team/go-webp)](https://goreportcard.com/report/github.com/kolesa-team/go-webp)\n[![codecov](https://codecov.io/gh/kolesa-team/go-webp/branch/master/graph/badge.svg)](https://codecov.io/gh/kolesa-team/go-webp)\n\nGolang Webp library for encoding and decoding, using **C** binding for Google libwebp\n\n## Requirements\n[libwebp](https://developers.google.com/speed/webp/docs/api)\n\n## Benchmarks\n```text\n% go test -bench \"^BenchmarkDecode\" ./webp                                                                                \ngoos: darwin\ngoarch: amd64\npkg: github.com/kolesa-team/go-webp/webp\nBenchmarkDecodeLossy-12                       45          25965139 ns/op\nBenchmarkDecodeXImageLossy-12                 13          90735879 ns/op\nBenchmarkDecodeLossless-12                    64          18887482 ns/op\nBenchmarkDecodeXImageLossless-12              27          42422596 ns/op\nPASS\nok      github.com/kolesa-team/go-webp/webp     7.877s\n```\n\n## Install libwebp\n#### MacOS:\n```bash\nbrew install webp\n```\n#### Linux:\n```bash\nsudo apt-get update\nsudo apt-get install libwebp-dev\n```\n\n## Install\n`go get -u github.com/kolesa-team/go-webp`\n\n## Examples\n\n#### Decode:\n```go\npackage main\n\nimport (\n\t\"image/jpeg\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/kolesa-team/go-webp/decoder\"\n\t\"github.com/kolesa-team/go-webp/webp\"\n)\n\nfunc main() {\n\tfile, err := os.Open(\"test_data/images/m4_q75.webp\")\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\toutput, err := os.Create(\"example/output_decode.jpg\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer output.Close()\n\n\timg, err := webp.Decode(file, &decoder.Options{})\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tif err = jpeg.Encode(output, img, &jpeg.Options{Quality:75}); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n```\n\n```bash\ngo run example/decode/main.go\n```\n\n#### Encode\n```go\npackage main\n\nimport (\n\t\"github.com/kolesa-team/go-webp/encoder\"\n\t\"github.com/kolesa-team/go-webp/webp\"\n\t\"image/jpeg\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc main() {\n\tfile, err := os.Open(\"test_data/images/source.jpg\")\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\timg, err := jpeg.Decode(file)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\toutput, err := os.Create(\"example/output_decode.webp\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer output.Close()\n\n\toptions, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tif err := webp.Encode(output, img, options); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n```\n```bash\ngo run example/encode/main.go\n```\n\n## TODO\n- return aux stats\n- container api\n- incremental decoding\n\n## License\nMIT licensed. See the LICENSE file for details.\n\n"
  },
  {
    "path": "_examples/decode/main.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"image/jpeg\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/kolesa-team/go-webp/decoder\"\n\t\"github.com/kolesa-team/go-webp/webp\"\n)\n\nfunc main() {\n\tfile, err := os.Open(\"test_data/images/m4_q75.webp\")\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\toutput, err := os.Create(\"example/output_decode.jpg\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t//noinspection GoUnhandledErrorResult\n\tdefer output.Close()\n\n\timg, err := webp.Decode(file, &decoder.Options{})\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tif err = jpeg.Encode(output, img, &jpeg.Options{Quality: 75}); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n"
  },
  {
    "path": "_examples/encode/main.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage main\n\nimport (\n\t\"github.com/kolesa-team/go-webp/encoder\"\n\t\"github.com/kolesa-team/go-webp/webp\"\n\t\"image/jpeg\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc main() {\n\tfile, err := os.Open(\"test_data/images/source.jpg\")\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\timg, err := jpeg.Decode(file)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\toutput, err := os.Create(\"example/output_encode.webp\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t//noinspection GoUnhandledErrorResult\n\tdefer output.Close()\n\n\toptions, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\tif err := webp.Encode(output, img, options); err != nil {\n\t\tlog.Fatalln(err)\n\t}\n}\n"
  },
  {
    "path": "codecov.yml",
    "content": "ignore:\n  - \"_examples\"\n  - \"utils\"\n  - \"test_data\"\ncoverage:\n  status:\n    project:\n      default:\n        target: 80%\n        threshold: 1%\n"
  },
  {
    "path": "decoder/decoder.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage decoder\n\n/*\n#cgo linux LDFLAGS: -lwebp\n#cgo darwin pkg-config: libwebp\n#cgo windows LDFLAGS: -lwebp\n#include <stdlib.h>\n#include <webp/decode.h>\n*/\nimport \"C\"\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"image\"\n\t\"io\"\n\t\"unsafe\"\n\n\t\"github.com/kolesa-team/go-webp/utils\"\n)\n\n// Decoder stores information to decode picture\ntype Decoder struct {\n\tdata    []byte\n\toptions *Options\n\tconfig  *C.WebPDecoderConfig\n\tdPtr    *C.uint8_t\n\tsPtr    C.size_t\n}\n\n// NewDecoder return new decoder instance\nfunc NewDecoder(r io.Reader, options *Options) (d *Decoder, err error) {\n\tvar data []byte\n\n\tif data, err = io.ReadAll(r); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(data) == 0 {\n\t\treturn nil, errors.New(\"data is empty\")\n\t}\n\n\tif options == nil {\n\t\toptions = &Options{}\n\t}\n\td = &Decoder{data: data, options: options}\n\n\tif d.config, err = d.options.GetConfig(); err != nil {\n\t\treturn nil, err\n\t}\n\n\td.dPtr = (*C.uint8_t)(&d.data[0])\n\td.sPtr = (C.size_t)(len(d.data))\n\n\t// получаем WebPBitstreamFeatures\n\tif status := d.parseFeatures(d.dPtr, d.sPtr); status != utils.Vp8StatusOk {\n\t\treturn nil, errors.New(fmt.Sprintf(\"cannot fetch features: %s\", status.String()))\n\t}\n\n\treturn\n}\n\n// Decode picture from reader\nfunc (d *Decoder) Decode() (image.Image, error) {\n\t// вписываем размеры итоговой картинки\n\td.config.output.width, d.config.output.height = d.getOutputDimensions()\n\t// указываем что декодируем в RGBA\n\td.config.output.colorspace = C.MODE_RGBA\n\td.config.output.is_external_memory = 1\n\n\timg := image.NewNRGBA(image.Rectangle{Max: image.Point{\n\t\tX: int(d.config.output.width),\n\t\tY: int(d.config.output.height),\n\t}})\n\n\tbuff := (*C.WebPRGBABuffer)(unsafe.Pointer(&d.config.output.u[0]))\n\tbuff.stride = C.int(img.Stride)\n\tbuff.rgba = (*C.uint8_t)(&img.Pix[0])\n\tbuff.size = (C.size_t)(len(img.Pix))\n\n\tif status := utils.VP8StatusCode(C.WebPDecode(d.dPtr, d.sPtr, d.config)); status != utils.Vp8StatusOk {\n\t\treturn nil, errors.New(fmt.Sprintf(\"cannot decode picture: %s\", status.String()))\n\t}\n\n\treturn img, nil\n}\n\n// GetFeatures return information about picture: width, height ...\nfunc (d *Decoder) GetFeatures() utils.BitstreamFeatures {\n\treturn utils.BitstreamFeatures{\n\t\tWidth:        int(d.config.input.width),\n\t\tHeight:       int(d.config.input.height),\n\t\tHasAlpha:     int(d.config.input.has_alpha) == 1,\n\t\tHasAnimation: int(d.config.input.has_animation) == 1,\n\t\tFormat:       utils.FormatType(d.config.input.format),\n\t}\n}\n\n// parse features from picture\nfunc (d *Decoder) parseFeatures(dataPtr *C.uint8_t, sizePtr C.size_t) utils.VP8StatusCode {\n\treturn utils.VP8StatusCode(C.WebPGetFeatures(dataPtr, sizePtr, &d.config.input))\n}\n\n// return dimensions of result image\nfunc (d *Decoder) getOutputDimensions() (width, height C.int) {\n\twidth = d.config.input.width\n\theight = d.config.input.height\n\n\tif d.config.options.use_scaling > 0 {\n\t\twidth = d.config.options.scaled_width\n\t\theight = d.config.options.scaled_height\n\t} else if d.config.options.use_cropping > 0 {\n\t\twidth = d.config.options.crop_width\n\t\theight = d.config.options.crop_height\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "decoder/decoder_test.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage decoder\n\nimport (\n\t\"github.com/kolesa-team/go-webp/utils\"\n\t\"github.com/stretchr/testify/require\"\n\t\"image\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestNewDecoder(t *testing.T) {\n\tt.Run(\"create success\", func(t *testing.T) {\n\t\tfile, err := os.Open(\"../test_data/images/m4_q75.webp\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif d, err := NewDecoder(file, &Options{\n\t\t\tBypassFiltering:   true,\n\t\t\tNoFancyUpsampling: true,\n\t\t\tScale: image.Rectangle{\n\t\t\t\tMax: image.Point{\n\t\t\t\t\tX: 300,\n\t\t\t\t\tY: 300,\n\t\t\t\t},\n\t\t\t},\n\t\t\tUseThreads:             true,\n\t\t\tFlip:                   true,\n\t\t\tDitheringStrength:      0,\n\t\t\tAlphaDitheringStrength: 0,\n\t\t}); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if img, err := d.Decode(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if img.Bounds().Max.X <= 0 || img.Bounds().Max.Y <= 0 {\n\t\t\tt.Fatal(\"invalid decoding result\")\n\t\t}\n\t})\n\tt.Run(\"empty file\", func(t *testing.T) {\n\t\tfile, err := os.Open(\"../test_data/images/invalid.webp\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif _, err := NewDecoder(file, &Options{\n\t\t\tCrop: image.Rectangle{\n\t\t\t\tMax: image.Point{\n\t\t\t\t\tX: 150,\n\t\t\t\t\tY: 150,\n\t\t\t\t},\n\t\t\t},\n\t\t}); err == nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestDecoder_GetFeatures(t *testing.T) {\n\tfile, err := os.Open(\"../test_data/images/m4_q75.webp\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdec, err := NewDecoder(file, &Options{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfeatures := dec.GetFeatures()\n\n\tif features.Width != 675 || features.Height != 900 {\n\t\tt.Fatal(\"incorrect dimensions\")\n\t}\n\n\tif features.Format != utils.FormatLossy {\n\t\tt.Fatal(\"file format is invalid\")\n\t}\n\n\tif features.HasAlpha {\n\t\tt.Fatal(\"file has_alpha is invalid\")\n\t}\n\n\tif features.HasAlpha {\n\t\tt.Fatal(\"file has_animation is invalid\")\n\t}\n}\n\nfunc TestDecoder_NilOptions(t *testing.T) {\n\tfile, err := os.Open(\"../test_data/images/m4_q75.webp\")\n\trequire.NoError(t, err)\n\n\t_, err = NewDecoder(file, nil)\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "decoder/options.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage decoder\n\n/*\n#cgo LDFLAGS: -lwebp\n#include <webp/decode.h>\n*/\nimport \"C\"\nimport (\n\t\"errors\"\n\t\"image\"\n)\n\n// Options specifies webp decoding parameters\ntype Options struct {\n\tBypassFiltering        bool\n\tNoFancyUpsampling      bool\n\tCrop                   image.Rectangle\n\tScale                  image.Rectangle\n\tUseThreads             bool\n\tFlip                   bool\n\tDitheringStrength      int\n\tAlphaDitheringStrength int\n}\n\n// GetConfig build WebPDecoderConfig for libwebp\nfunc (o *Options) GetConfig() (*C.WebPDecoderConfig, error) {\n\tconfig := C.WebPDecoderConfig{}\n\n\tif C.WebPInitDecoderConfig(&config) == 0 {\n\t\treturn nil, errors.New(\"cannot init decoder config\")\n\t}\n\n\tif o.BypassFiltering {\n\t\tconfig.options.bypass_filtering = 1\n\t}\n\n\tif o.NoFancyUpsampling {\n\t\tconfig.options.no_fancy_upsampling = 1\n\t}\n\n\t// проверяем надо ли кропнуть\n\tif o.Crop.Max.X > 0 && o.Crop.Max.Y > 0 {\n\t\tconfig.options.use_cropping = 1\n\t\tconfig.options.crop_left = C.int(o.Crop.Min.X)\n\t\tconfig.options.crop_top = C.int(o.Crop.Min.Y)\n\t\tconfig.options.crop_width = C.int(o.Crop.Max.X)\n\t\tconfig.options.crop_height = C.int(o.Crop.Max.Y)\n\t}\n\n\t// проверяем надо ли заскейлить\n\tif o.Scale.Max.X > 0 && o.Scale.Max.Y > 0 {\n\t\tconfig.options.use_scaling = 1\n\t\tconfig.options.scaled_width = C.int(o.Scale.Max.X)\n\t\tconfig.options.scaled_height = C.int(o.Scale.Max.Y)\n\t}\n\n\tif o.UseThreads {\n\t\tconfig.options.use_threads = 1\n\t}\n\n\tconfig.options.dithering_strength = C.int(o.DitheringStrength)\n\n\tif o.Flip {\n\t\tconfig.options.flip = 1\n\t}\n\n\tconfig.options.alpha_dithering_strength = C.int(o.AlphaDitheringStrength)\n\n\treturn &config, nil\n}\n"
  },
  {
    "path": "decoder/options_test.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage decoder\n\nimport (\n\t\"image\"\n\t\"testing\"\n)\n\nfunc TestOptions_GetConfig(t *testing.T) {\n\tt.Run(\"check crop\", func(t *testing.T) {\n\t\toptions := &Options{\n\t\t\tCrop: image.Rectangle{\n\t\t\t\tMin: image.Point{X: 100, Y: 200},\n\t\t\t\tMax: image.Point{X: 400, Y: 500},\n\t\t\t},\n\t\t}\n\t\tif cfg, err := options.GetConfig(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if cfg.options.use_cropping != 1 {\n\t\t\tt.Fatal(\"cropping is disabled\")\n\t\t} else if cfg.options.crop_left != 100 {\n\t\t\tt.Fatal(\"crop_left is invalid\")\n\t\t} else if cfg.options.crop_top != 200 {\n\t\t\tt.Fatal(\"crop_top is invalid\")\n\t\t} else if cfg.options.crop_width != 400 {\n\t\t\tt.Fatal(\"crop_width is invalid\")\n\t\t} else if cfg.options.crop_height != 500 {\n\t\t\tt.Fatal(\"crop_height is invalid\")\n\t\t}\n\t})\n\tt.Run(\"check scale\", func(t *testing.T) {\n\t\toptions := &Options{\n\t\t\tScale: image.Rectangle{\n\t\t\t\tMax: image.Point{X: 400, Y: 500},\n\t\t\t},\n\t\t}\n\t\tif cfg, err := options.GetConfig(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if cfg.options.use_scaling != 1 {\n\t\t\tt.Fatal(\"scaling is disabled\")\n\t\t} else if cfg.options.scaled_width != 400 {\n\t\t\tt.Fatal(\"scaled_width is invalid\")\n\t\t} else if cfg.options.scaled_height != 500 {\n\t\t\tt.Fatal(\"scaled_height is invalid\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "encoder/encoder.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage encoder\n\n/*\n\n#cgo linux LDFLAGS: -lwebp\n#cgo darwin pkg-config: libwebp\n#cgo windows LDFLAGS: -lwebp\n#include <stdlib.h>\n#include <webp/encode.h>\nstatic uint8_t* encodeNRBBA(WebPConfig* config, const uint8_t* rgba, int width, int height, int stride, size_t* output_size) {\n\tWebPPicture pic;\n\tWebPMemoryWriter wrt;\n\tint ok;\n\n\tif (!WebPPictureInit(&pic)) {\n\t\treturn NULL;\n\t}\n\n\tpic.use_argb = 1;\n\tpic.width = width;\n\tpic.height = height;\n\tpic.writer = WebPMemoryWrite;\n\tpic.custom_ptr = &wrt;\n\tWebPMemoryWriterInit(&wrt);\n\n\tok = WebPPictureImportRGBA(&pic, rgba, stride) && WebPEncode(config, &pic);\n\tWebPPictureFree(&pic);\n\n\tif (!ok) {\n\t\tWebPMemoryWriterClear(&wrt);\n\t\treturn NULL;\n\t}\n\n\t*output_size = wrt.size;\n\treturn wrt.mem;\n}\n*/\nimport \"C\"\nimport (\n\t\"errors\"\n\t\"image\"\n\t\"image/draw\"\n\t\"io\"\n\t\"unsafe\"\n)\n\n// Encoder stores information to encode image\ntype Encoder struct {\n\toptions *Options\n\tconfig  *C.WebPConfig\n\timg     *image.NRGBA\n}\n\n// NewEncoder return new encoder instance\nfunc NewEncoder(src image.Image, options *Options) (e *Encoder, err error) {\n\tvar config *C.WebPConfig\n\n\tif options == nil {\n\t\toptions, _ = NewLossyEncoderOptions(PresetDefault, 75)\n\t}\n\tif config, err = options.GetConfig(); err != nil {\n\t\treturn nil, err\n\t}\n\n\te = &Encoder{options: options, config: config}\n\n\tswitch v := src.(type) {\n\tcase *image.NRGBA:\n\t\te.img = v\n\tdefault:\n\t\te.img = e.convertToNRGBA(src)\n\t}\n\n\treturn\n}\n\n// Encode picture and flush to io.Writer\nfunc (e *Encoder) Encode(w io.Writer) error {\n\tvar size C.size_t\n\n\toutput := C.encodeNRBBA(\n\t\te.config,\n\t\t(*C.uint8_t)(&e.img.Pix[0]),\n\t\tC.int(e.img.Rect.Dx()),\n\t\tC.int(e.img.Rect.Dy()),\n\t\tC.int(e.img.Stride),\n\t\t&size,\n\t)\n\n\tif output == nil || size == 0 {\n\t\treturn errors.New(\"cannot encode webppicture\")\n\t}\n\tdefer C.free(unsafe.Pointer(output))\n\n\t_, err := w.Write(((*[1 << 30]byte)(unsafe.Pointer(output)))[0:int(size):int(size)])\n\n\treturn err\n}\n\n// Convert picture from any image.Image type to *image.NRGBA\n// @todo optimization needed\nfunc (e *Encoder) convertToNRGBA(src image.Image) (dst *image.NRGBA) {\n\tdst = image.NewNRGBA(src.Bounds())\n\tdraw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)\n\n\treturn\n}\n"
  },
  {
    "path": "encoder/encoder_test.go",
    "content": "package encoder\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"image\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestNewEncoder(t *testing.T) {\n\tt.Run(\"encode nrgba image with lossy preset\", func(t *testing.T) {\n\t\texpected := &bytes.Buffer{}\n\t\timg := image.NewNRGBA(image.Rectangle{\n\t\t\tMax: image.Point{\n\t\t\t\tX: 100,\n\t\t\t\tY: 150,\n\t\t\t},\n\t\t})\n\n\t\toptions, err := NewLossyEncoderOptions(PresetDefault, 0.75)\n\t\trequire.NoError(t, err)\n\n\t\te, err := NewEncoder(img, options)\n\t\trequire.NoError(t, err)\n\n\t\terr = e.Encode(expected)\n\t\trequire.NoError(t, err)\n\n\t\tactual, err := os.ReadFile(\"../test_data/images/100x150_lossy.webp\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, actual, expected.Bytes())\n\t})\n\tt.Run(\"encode nrgba image with lossless preset\", func(t *testing.T) {\n\t\tactuall := &bytes.Buffer{}\n\t\timg := image.NewNRGBA(image.Rectangle{\n\t\t\tMax: image.Point{\n\t\t\t\tX: 100,\n\t\t\t\tY: 150,\n\t\t\t},\n\t\t})\n\n\t\toptions, err := NewLosslessEncoderOptions(PresetDefault, 1)\n\t\trequire.NoError(t, err)\n\n\t\te, err := NewEncoder(img, options)\n\t\trequire.NoError(t, err)\n\n\t\terr = e.Encode(actuall)\n\t\trequire.NoError(t, err)\n\n\t\texpected, err := os.ReadFile(\"../test_data/images/100x150_lossless.webp\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, expected, actuall.Bytes())\n\t})\n}\n\nfunc TestNewEncoder_NilOptions(t *testing.T) {\n\tt.Run(\"encode image without passing options should not panic\", func(t *testing.T) {\n\t\texpected := &bytes.Buffer{}\n\t\timg := image.NewNRGBA(image.Rectangle{\n\t\t\tMax: image.Point{\n\t\t\t\tX: 100,\n\t\t\t\tY: 150,\n\t\t\t},\n\t\t})\n\n\t\te, err := NewEncoder(img, nil)\n\t\trequire.NoError(t, err)\n\n\t\terr = e.Encode(expected)\n\t\trequire.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "encoder/options.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage encoder\n\n/*\n#cgo LDFLAGS: -lwebp\n#include <webp/encode.h>\n*/\nimport \"C\"\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n// Default libwebp image hints\n//noinspection GoUnusedConst\nconst (\n\tHintDefault ImageHint = iota\n\tHintPicture\n\tHintPhoto\n\tHintGraph\n\tHintLast\n)\n\n// Default libwebp presets\n//noinspection GoUnusedConst\nconst (\n\tPresetDefault EncodingPreset = iota\n\tPresetPicture\n\tPresetPhoto\n\tPresetDrawing\n\tPresetIcon\n\tPresetText\n)\n\ntype (\n\t// ImageHint hint of picture\n\tImageHint int\n\t// EncodingPreset using Preset\n\tEncodingPreset int\n\t// Options specifies webp encoding parameters\n\tOptions struct {\n\t\tconfig *C.WebPConfig\n\n\t\tLossless         bool\n\t\tQuality          float32\n\t\tMethod           int\n\t\tImageHint        ImageHint\n\t\tTargetSize       int\n\t\tTargetPsnr       float32\n\t\tSegments         int\n\t\tSnsStrength      int\n\t\tFilterStrength   int\n\t\tFilterSharpness  int\n\t\tFilterType       int\n\t\tAutofilter       bool\n\t\tAlphaCompression int\n\t\tAlphaFiltering   int\n\t\talphaQuality     int\n\t\tPass             int\n\t\t// Disabled for compatibility with old version libwebp\n\t\t// QMin             int\n\t\t// QMax             int\n\t\tShowCompressed  bool\n\t\tPreprocessing   int\n\t\tPartitions      int\n\t\tPartitionLimit  int\n\t\tEmulateJpegSize bool\n\t\tThreadLevel     bool\n\t\tLowMemory       bool\n\t\tNearLossless    int\n\t\tExact           int\n\t\tUseDeltaPalette bool\n\t\tUseSharpYuv     bool\n\t}\n)\n\n// NewLossyEncoderOptions build lossy encoding options\nfunc NewLossyEncoderOptions(preset EncodingPreset, quality float32) (options *Options, err error) {\n\toptions = &Options{\n\t\tconfig: &C.WebPConfig{},\n\t}\n\n\tif C.WebPConfigPreset(options.config, C.WebPPreset(preset), C.float(quality)) == 0 {\n\t\treturn nil, errors.New(\"cannot init encoder config\")\n\t}\n\n\toptions.sync()\n\n\treturn\n}\n\n// NewLosslessEncoderOptions build lossless encoding options\nfunc NewLosslessEncoderOptions(preset EncodingPreset, level int) (options *Options, err error) {\n\tif options, err = NewLossyEncoderOptions(preset, 0); err != nil {\n\t\treturn\n\t}\n\tif C.WebPConfigLosslessPreset(options.config, C.int(level)) == 0 {\n\t\treturn nil, errors.New(\"cannot init lossless preset\")\n\t}\n\n\toptions.sync()\n\n\treturn\n}\n\nfunc (o *Options) sync() {\n\to.Lossless = o.config.lossless == 1\n\to.Quality = float32(o.config.quality)\n\to.Method = int(o.config.method)\n\to.ImageHint = ImageHint(o.config.image_hint)\n\to.TargetSize = int(o.config.target_size)\n\to.TargetPsnr = float32(o.config.target_PSNR)\n\to.Segments = int(o.config.segments)\n\to.SnsStrength = int(o.config.sns_strength)\n\to.FilterStrength = int(o.config.filter_strength)\n\to.FilterSharpness = int(o.config.filter_sharpness)\n\to.FilterType = int(o.config.filter_type)\n\to.Autofilter = o.config.autofilter == 1\n\to.AlphaCompression = int(o.config.alpha_compression)\n\to.AlphaFiltering = int(o.config.alpha_filtering)\n\to.alphaQuality = int(o.config.alpha_quality)\n\to.Pass = int(o.config.pass)\n\t// Disabled for compatibility with old version libwebp\n\t// o.QMin = int(o.config.qmin)\n\t// o.QMax = int(o.config.qmax)\n\to.ShowCompressed = o.config.show_compressed == 1\n\to.Preprocessing = int(o.config.preprocessing)\n\to.Partitions = int(o.config.partitions)\n\to.PartitionLimit = int(o.config.partition_limit)\n\to.EmulateJpegSize = o.config.emulate_jpeg_size == 1\n\to.ThreadLevel = o.config.thread_level == 1\n\to.LowMemory = o.config.low_memory == 1\n\to.NearLossless = int(o.config.near_lossless)\n\to.Exact = int(o.config.exact)\n\to.UseDeltaPalette = o.config.use_delta_palette == 1\n\to.UseSharpYuv = o.config.use_sharp_yuv == 1\n}\n\nfunc (o *Options) boolToCInt(expression bool) (result C.int) {\n\tresult = 0\n\n\tif expression {\n\t\tresult = 1\n\t}\n\n\treturn\n}\n\n// GetConfig build WebPConfig for libwebp\nfunc (o *Options) GetConfig() (*C.WebPConfig, error) {\n\tvar err error\n\tif o == nil {\n\t\to, err = NewLosslessEncoderOptions(PresetDefault, 0)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot validate default config: [%w]\", err)\n\t\t}\n\t}\n\to.config.lossless = o.boolToCInt(o.Lossless)\n\to.config.quality = C.float(o.Quality)\n\to.config.method = C.int(o.Method)\n\to.config.image_hint = C.WebPImageHint(o.ImageHint)\n\to.config.target_size = C.int(o.TargetSize)\n\to.config.target_PSNR = C.float(o.TargetPsnr)\n\to.config.segments = C.int(o.Segments)\n\to.config.sns_strength = C.int(o.SnsStrength)\n\to.config.filter_strength = C.int(o.FilterStrength)\n\to.config.filter_sharpness = C.int(o.FilterSharpness)\n\to.config.filter_type = C.int(o.FilterType)\n\to.config.autofilter = o.boolToCInt(o.Autofilter)\n\to.config.alpha_compression = C.int(o.AlphaCompression)\n\to.config.alpha_filtering = C.int(o.AlphaFiltering)\n\to.config.alpha_quality = C.int(o.alphaQuality)\n\to.config.pass = C.int(o.Pass)\n\t// Disabled for compatibility with old version libwebp\n\t// o.config.qmin = C.int(o.QMin)\n\t// o.config.qmax = C.int(o.QMax)\n\to.config.show_compressed = o.boolToCInt(o.ShowCompressed)\n\to.config.preprocessing = C.int(o.Preprocessing)\n\to.config.partitions = C.int(o.Partitions)\n\to.config.partition_limit = C.int(o.PartitionLimit)\n\to.config.emulate_jpeg_size = o.boolToCInt(o.EmulateJpegSize)\n\to.config.thread_level = o.boolToCInt(o.ThreadLevel)\n\to.config.low_memory = o.boolToCInt(o.LowMemory)\n\to.config.near_lossless = C.int(o.NearLossless)\n\to.config.exact = C.int(o.Exact)\n\to.config.use_delta_palette = o.boolToCInt(o.UseDeltaPalette)\n\to.config.use_sharp_yuv = o.boolToCInt(o.UseSharpYuv)\n\n\tif C.WebPValidateConfig(o.config) == 0 {\n\t\treturn nil, errors.New(\"cannot validate config\")\n\t}\n\n\treturn o.config, nil\n}\n"
  },
  {
    "path": "encoder/options_test.go",
    "content": "package encoder\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"testing\"\n)\n\nfunc TestNilOptions(t *testing.T) {\n\tt.Run(\"config from nil Options succeeds\", func(t *testing.T) {\n\t\tvar options *Options = nil\n\t\tcfg, err := options.GetConfig()\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, cfg)\n\t})\n}\n\nfunc TestNewLossyEncoderOptions(t *testing.T) {\n\tt.Run(\"invalid quality\", func(t *testing.T) {\n\t\toptions, err := NewLossyEncoderOptions(PresetDefault, -1)\n\t\trequire.Error(t, err)\n\t\tassert.Nil(t, options)\n\t})\n\tt.Run(\"create lossy encoder options is success\", func(t *testing.T) {\n\t\toptions, err := NewLossyEncoderOptions(PresetDefault, 75)\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, options)\n\n\t\tassert.False(t, options.Lossless)\n\t\tassert.Equal(t, float32(75), options.Quality)\n\n\t\tcfg, err := options.GetConfig()\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, cfg)\n\t})\n}\n\nfunc TestNewLosslessEncoderOptions(t *testing.T) {\n\tt.Run(\"invalid level of lossless encoder\", func(t *testing.T) {\n\t\toptions, err := NewLosslessEncoderOptions(PresetDefault, -1)\n\t\trequire.Error(t, err)\n\t\tassert.Nil(t, options)\n\t})\n\tt.Run(\"create lossless encoder options is success\", func(t *testing.T) {\n\t\toptions, err := NewLosslessEncoderOptions(PresetDefault, 1)\n\t\trequire.Nil(t, err)\n\t\trequire.NotNil(t, options)\n\n\t\tassert.True(t, options.Lossless)\n\t\tassert.Equal(t, 1, options.Method)\n\t\tassert.Equal(t, float32(20), options.Quality)\n\n\t\tcfg, err := options.GetConfig()\n\t\trequire.NoError(t, err)\n\t\tassert.NotNil(t, cfg)\n\t})\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/kolesa-team/go-webp\n\ngo 1.10\n\nrequire github.com/stretchr/testify v1.7.0\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "utils/vp8.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage utils\n\n//noinspection GoUnusedConst\nconst (\n\tVp8StatusOk VP8StatusCode = iota\n\tVp8StatusOutOfMemory\n\tVp8StatusInvalidParam\n\tVp8StatusBitstreamError\n\tVp8StatusUnsupportedFeature\n\tVp8StatusSuspended\n\tVp8StatusUserAbort\n\tVp8StatusNotEnoughData\n)\n\ntype VP8StatusCode int\n\nfunc (c VP8StatusCode) String() (label string) {\n\tswitch c {\n\tcase Vp8StatusOk:\n\t\tlabel = \"VP8_STATUS_OK\"\n\tcase Vp8StatusOutOfMemory:\n\t\tlabel = \"VP8_STATUS_OUT_OF_MEMORY\"\n\tcase Vp8StatusInvalidParam:\n\t\tlabel = \"VP8_STATUS_INVALID_PARAM\"\n\tcase Vp8StatusBitstreamError:\n\t\tlabel = \"VP8_STATUS_BITSTREAM_ERROR\"\n\tcase Vp8StatusUnsupportedFeature:\n\t\tlabel = \"VP8_STATUS_UNSUPPORTED_FEATURE\"\n\tcase Vp8StatusSuspended:\n\t\tlabel = \"VP8_STATUS_SUSPENDED\"\n\tcase Vp8StatusUserAbort:\n\t\tlabel = \"VP8_STATUS_USER_ABORT\"\n\tcase Vp8StatusNotEnoughData:\n\t\tlabel = \"VP8_STATUS_NOT_ENOUGH_DATA\"\n\tdefault:\n\t\tlabel = \"VP8 undefined status code\"\n\t}\n\n\treturn\n}\n\nconst (\n\tVp8EncOk Vp8EncStatus = iota\n\tVp8EncErrorOutOfMemory\n\tVp8EncErrorBitstreamOutOfMemory\n\tVp8EncErrorNullParameter\n\tVp8EncErrorInvalidConfiguration\n\tVp8EncErrorBadDimension\n\tVp8EncErrorPartition0Overflow\n\tVp8EncErrorPartitionOverflow\n\tVp8EncErrorBadWrite\n\tVp8EncErrorFileTooBig\n\tVp8EncErrorUserAbort\n\tVp8EncErrorLast\n)\n\ntype Vp8EncStatus int\n\nfunc (c Vp8EncStatus) String() (label string) {\n\tswitch c {\n\tcase Vp8EncOk:\n\t\tlabel = \"VP8_ENC_OK\"\n\tcase Vp8EncErrorOutOfMemory:\n\t\tlabel = \"VP8_ENC_ERROR_OUT_OF_MEMORY\"\n\tcase Vp8EncErrorBitstreamOutOfMemory:\n\t\tlabel = \"VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY\"\n\tcase Vp8EncErrorNullParameter:\n\t\tlabel = \"VP8_ENC_ERROR_NULL_PARAMETER\"\n\tcase Vp8EncErrorInvalidConfiguration:\n\t\tlabel = \"VP8_ENC_ERROR_INVALID_CONFIGURATION\"\n\tcase Vp8EncErrorBadDimension:\n\t\tlabel = \"VP8_ENC_ERROR_BAD_DIMENSION\"\n\tcase Vp8EncErrorPartition0Overflow:\n\t\tlabel = \"VP8_ENC_ERROR_PARTITION0_OVERFLOW\"\n\tcase Vp8EncErrorPartitionOverflow:\n\t\tlabel = \"VP8_ENC_ERROR_PARTITION_OVERFLOW\"\n\tcase Vp8EncErrorBadWrite:\n\t\tlabel = \"VP8_ENC_ERROR_BAD_WRITE\"\n\tcase Vp8EncErrorFileTooBig:\n\t\tlabel = \"VP8_ENC_ERROR_FILE_TOO_BIG\"\n\tcase Vp8EncErrorUserAbort:\n\t\tlabel = \"VP8_ENC_ERROR_USER_ABORT\"\n\tcase Vp8EncErrorLast:\n\t\tlabel = \"VP8_ENC_ERROR_LAST\"\n\tdefault:\n\t\tlabel = \"VP8 undefined status code\"\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "utils/webp.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage utils\n\ntype FormatType int\n\n//noinspection GoUnusedConst\nconst (\n\tFormatUndefined FormatType = iota\n\tFormatLossy\n\tFormatLossless\n)\n\ntype BitstreamFeatures struct {\n\tWidth        int\n\tHeight       int\n\tHasAlpha     bool\n\tHasAnimation bool\n\tFormat       FormatType\n}\n"
  },
  {
    "path": "webp/webp.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage webp\n\nimport (\n\t\"github.com/kolesa-team/go-webp/decoder\"\n\t\"github.com/kolesa-team/go-webp/encoder\"\n\t\"image\"\n\t\"image/color\"\n\t\"io\"\n)\n\nfunc init() {\n\timage.RegisterFormat(\"webp\", \"RIFF????WEBPVP8\", quickDecode, quickDecodeConfig)\n}\n\nfunc quickDecode(r io.Reader) (image.Image, error) {\n\treturn Decode(r, &decoder.Options{})\n}\n\nfunc quickDecodeConfig(r io.Reader) (image.Config, error) {\n\treturn DecodeConfig(r, &decoder.Options{})\n}\n\n// Decode picture from reader\nfunc Decode(r io.Reader, options *decoder.Options) (image.Image, error) {\n\tif dec, err := decoder.NewDecoder(r, options); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn dec.Decode()\n\t}\n}\n\nfunc DecodeConfig(r io.Reader, options *decoder.Options) (image.Config, error) {\n\tif dec, err := decoder.NewDecoder(r, options); err != nil {\n\t\treturn image.Config{}, err\n\t} else {\n\t\treturn image.Config{\n\t\t\tColorModel: color.RGBAModel,\n\t\t\tWidth:      dec.GetFeatures().Width,\n\t\t\tHeight:     dec.GetFeatures().Height,\n\t\t}, nil\n\t}\n}\n\n// Encode picture and write to io.Writer\nfunc Encode(w io.Writer, src image.Image, options *encoder.Options) error {\n\tif enc, err := encoder.NewEncoder(src, options); err != nil {\n\t\treturn err\n\t} else {\n\t\treturn enc.Encode(w)\n\t}\n}\n"
  },
  {
    "path": "webp/webp_test.go",
    "content": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of\n// this software and associated documentation files (the \"Software\"), to deal in\n// the Software without restriction, including without limitation the rights to\n// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n// the Software, and to permit persons to whom the Software is furnished to do so,\n// subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage webp\n\nimport (\n\t\"bytes\"\n\t\"github.com/kolesa-team/go-webp/decoder\"\n\t\"github.com/kolesa-team/go-webp/encoder\"\n\t\"github.com/stretchr/testify/require\"\n\t\"image\"\n\t\"image/color\"\n\t\"image/jpeg\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestEncode(t *testing.T) {\n\tt.Run(\"encode lossy\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/source.jpg\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\timg, err := jpeg.Decode(r)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\toptions, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif err = Encode(io.Discard, img, options); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tt.Run(\"encode lossless\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/source.jpg\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\timg, err := jpeg.Decode(r)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\toptions, err := encoder.NewLosslessEncoderOptions(encoder.PresetDefault, 4)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif err = Encode(io.Discard, img, options); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\tt.Run(\"failed encoding with large size\", func(t *testing.T) {\n\t\timg := image.NewNRGBA(image.Rectangle{\n\t\t\tMax: image.Point{X: 20000, Y: 1},\n\t\t})\n\n\t\toptions, err := encoder.NewLosslessEncoderOptions(encoder.PresetDefault, 4)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif err = Encode(io.Discard, img, options); err == nil {\n\t\t\tt.Fatal(\"an error was expected\")\n\t\t}\n\t})\n}\n\nfunc TestDecode(t *testing.T) {\n\tt.Run(\"decode lossy webp picture with many options\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/webp-logo-lossy.webp\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif img, err := Decode(r, &decoder.Options{\n\t\t\tBypassFiltering:   true,\n\t\t\tNoFancyUpsampling: true,\n\n\t\t\tCrop: image.Rectangle{},\n\t\t\tScale: image.Rectangle{\n\t\t\t\tMax: image.Point{\n\t\t\t\t\tX: 400,\n\t\t\t\t\tY: 300,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\tUseThreads:             true,\n\t\t\tFlip:                   true,\n\t\t\tDitheringStrength:      1,\n\t\t\tAlphaDitheringStrength: 1,\n\t\t}); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if img == nil {\n\t\t\tt.Fatal(\"img is empty\")\n\t\t} else if img.Bounds().Max.X != 400 || img.Bounds().Max.Y != 300 {\n\t\t\tt.Fatal(\"img is invalid\")\n\t\t}\n\t})\n\tt.Run(\"decode lossless webp picture\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/webp-logo-lossless.webp\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif img, err := Decode(r, &decoder.Options{}); err != nil {\n\t\t\tt.Fatal(err)\n\t\t} else if img == nil {\n\t\t\tt.Fatal(\"img is empty\")\n\t\t}\n\t})\n\tt.Run(\"decode invalid webp file\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/invalid.webp\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif _, err := Decode(r, &decoder.Options{}); err == nil {\n\t\t\tt.Fatal(\"an error was expected\")\n\t\t}\n\t})\n\tt.Run(\"decode lossy image via standard image.Decode\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/webp-logo-lossy.webp\")\n\t\trequire.NoError(t, err)\n\t\timg, format, err := image.Decode(r)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, \"webp\", format)\n\t\trequire.Equal(t, color.NRGBAModel, img.ColorModel())\n\t})\n\tt.Run(\"decode lossless image via standard image.Decode\", func(t *testing.T) {\n\t\tr, err := os.Open(\"../test_data/images/webp-logo-lossless.webp\")\n\t\trequire.NoError(t, err)\n\t\timg, format, err := image.Decode(r)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, \"webp\", format)\n\t\trequire.Equal(t, color.NRGBAModel, img.ColorModel())\n\t})\n}\n\nfunc BenchmarkDecodeLossy(b *testing.B) {\n\tdata, err := os.ReadFile(\"../test_data/images/webp-logo-lossy.webp\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tdec, err := decoder.NewDecoder(bytes.NewBuffer(data), &decoder.Options{})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif _, err := dec.Decode(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkDecodeXImageLossy(b *testing.B) {\n\tdata, err := os.ReadFile(\"../test_data/images/webp-logo-lossy.webp\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err = Decode(bytes.NewBuffer(data), &decoder.Options{})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkDecodeLossless(b *testing.B) {\n\tdata, err := os.ReadFile(\"../test_data/images/webp-logo-lossless.webp\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tdec, err := decoder.NewDecoder(bytes.NewBuffer(data), &decoder.Options{})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif _, err := dec.Decode(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkDecodeXImageLossless(b *testing.B) {\n\tdata, err := os.ReadFile(\"../test_data/images/webp-logo-lossless.webp\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err = Decode(bytes.NewBuffer(data), &decoder.Options{})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkEncode(b *testing.B) {\n\tr, err := os.Open(\"../test_data/images/source.jpg\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\timg, err := jpeg.Decode(r)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\toptions, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif err = Encode(io.Discard, img, options); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n"
  }
]