Full Code of kolesa-team/go-webp for AI

master bf7924d9a4e2 cached
23 files
47.2 KB
13.6k tokens
84 symbols
1 requests
Download .txt
Repository: kolesa-team/go-webp
Branch: master
Commit: bf7924d9a4e2
Files: 23
Total size: 47.2 KB

Directory structure:
gitextract_hax6w2m8/

├── .gitignore
├── .travis.yml
├── AUTHORS
├── LICENSE
├── Makefile
├── README.md
├── _examples/
│   ├── decode/
│   │   └── main.go
│   └── encode/
│       └── main.go
├── codecov.yml
├── decoder/
│   ├── decoder.go
│   ├── decoder_test.go
│   ├── options.go
│   └── options_test.go
├── encoder/
│   ├── encoder.go
│   ├── encoder_test.go
│   ├── options.go
│   └── options_test.go
├── go.mod
├── go.sum
├── utils/
│   ├── vp8.go
│   └── webp.go
└── webp/
    ├── webp.go
    └── webp_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/.idea
/vendor
.DS_Store
/example/output*


================================================
FILE: .travis.yml
================================================
dist: bionic
language: go
go:
  - 1.13.x
  - 1.14.x
  - 1.15.x
  - 1.16.x
  - 1.x

script:
  - go vet ./...
  - go test -race -coverprofile=coverage.txt -covermode=atomic ./...

before_install:
  - sudo apt-get install libwebp-dev
  - go mod vendor -v

after_success:
  - bash <(curl -s https://codecov.io/bash)

addons:
  apt:
    update: true


================================================
FILE: AUTHORS
================================================
Google LLC (https://opensource.google.com/)
Amangeldy Kadyl <lex0.kz@gmail.com>


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2019 Amangeldy Kadyl

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: Makefile
================================================
.PHONY: vendor test

GO ?= $(which go)

test:
	@GO test ./... -v

vendor:
	@GO mod vendor

vet:
	@GO vet ./... -v

bench:
	@GO test -bench=. ./webp

libwebp-mac:
	brew install webp

libwebp-ubuntu:
	sudo apt-get install libwebp-dev


================================================
FILE: README.md
================================================
![logo](test_data/images/logo.jpg)
# go-webp
[![Build Status](https://api.travis-ci.com/kolesa-team/go-webp.svg?branch=master)](https://travis-ci.com/github/kolesa-team/go-webp)
[![GoDoc](https://godoc.org/github.com/kolesa-team/go-webp?status.svg)](https://godoc.org/github.com/kolesa-team/go-webp)
[![Go Report](https://goreportcard.com/badge/github.com/kolesa-team/go-webp)](https://goreportcard.com/report/github.com/kolesa-team/go-webp)
[![codecov](https://codecov.io/gh/kolesa-team/go-webp/branch/master/graph/badge.svg)](https://codecov.io/gh/kolesa-team/go-webp)

Golang Webp library for encoding and decoding, using **C** binding for Google libwebp

## Requirements
[libwebp](https://developers.google.com/speed/webp/docs/api)

## Benchmarks
```text
% go test -bench "^BenchmarkDecode" ./webp                                                                                
goos: darwin
goarch: amd64
pkg: github.com/kolesa-team/go-webp/webp
BenchmarkDecodeLossy-12                       45          25965139 ns/op
BenchmarkDecodeXImageLossy-12                 13          90735879 ns/op
BenchmarkDecodeLossless-12                    64          18887482 ns/op
BenchmarkDecodeXImageLossless-12              27          42422596 ns/op
PASS
ok      github.com/kolesa-team/go-webp/webp     7.877s
```

## Install libwebp
#### MacOS:
```bash
brew install webp
```
#### Linux:
```bash
sudo apt-get update
sudo apt-get install libwebp-dev
```

## Install
`go get -u github.com/kolesa-team/go-webp`

## Examples

#### Decode:
```go
package main

import (
	"image/jpeg"
	"log"
	"os"

	"github.com/kolesa-team/go-webp/decoder"
	"github.com/kolesa-team/go-webp/webp"
)

func main() {
	file, err := os.Open("test_data/images/m4_q75.webp")
	if err != nil {
		log.Fatalln(err)
	}

	output, err := os.Create("example/output_decode.jpg")
	if err != nil {
		log.Fatal(err)
	}
	defer output.Close()

	img, err := webp.Decode(file, &decoder.Options{})
	if err != nil {
		log.Fatalln(err)
	}

	if err = jpeg.Encode(output, img, &jpeg.Options{Quality:75}); err != nil {
		log.Fatalln(err)
	}
}
```

```bash
go run example/decode/main.go
```

#### Encode
```go
package main

import (
	"github.com/kolesa-team/go-webp/encoder"
	"github.com/kolesa-team/go-webp/webp"
	"image/jpeg"
	"log"
	"os"
)

func main() {
	file, err := os.Open("test_data/images/source.jpg")
	if err != nil {
		log.Fatalln(err)
	}

	img, err := jpeg.Decode(file)
	if err != nil {
		log.Fatalln(err)
	}

	output, err := os.Create("example/output_decode.webp")
	if err != nil {
		log.Fatal(err)
	}
	defer output.Close()

	options, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)
	if err != nil {
		log.Fatalln(err)
	}

	if err := webp.Encode(output, img, options); err != nil {
		log.Fatalln(err)
	}
}
```
```bash
go run example/encode/main.go
```

## TODO
- return aux stats
- container api
- incremental decoding

## License
MIT licensed. See the LICENSE file for details.



================================================
FILE: _examples/decode/main.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package main

import (
	"image/jpeg"
	"log"
	"os"

	"github.com/kolesa-team/go-webp/decoder"
	"github.com/kolesa-team/go-webp/webp"
)

func main() {
	file, err := os.Open("test_data/images/m4_q75.webp")
	if err != nil {
		log.Fatalln(err)
	}

	output, err := os.Create("example/output_decode.jpg")
	if err != nil {
		log.Fatal(err)
	}
	//noinspection GoUnhandledErrorResult
	defer output.Close()

	img, err := webp.Decode(file, &decoder.Options{})
	if err != nil {
		log.Fatalln(err)
	}

	if err = jpeg.Encode(output, img, &jpeg.Options{Quality: 75}); err != nil {
		log.Fatalln(err)
	}
}


================================================
FILE: _examples/encode/main.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package main

import (
	"github.com/kolesa-team/go-webp/encoder"
	"github.com/kolesa-team/go-webp/webp"
	"image/jpeg"
	"log"
	"os"
)

func main() {
	file, err := os.Open("test_data/images/source.jpg")
	if err != nil {
		log.Fatalln(err)
	}

	img, err := jpeg.Decode(file)
	if err != nil {
		log.Fatalln(err)
	}

	output, err := os.Create("example/output_encode.webp")
	if err != nil {
		log.Fatal(err)
	}
	//noinspection GoUnhandledErrorResult
	defer output.Close()

	options, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)
	if err != nil {
		log.Fatalln(err)
	}

	if err := webp.Encode(output, img, options); err != nil {
		log.Fatalln(err)
	}
}


================================================
FILE: codecov.yml
================================================
ignore:
  - "_examples"
  - "utils"
  - "test_data"
coverage:
  status:
    project:
      default:
        target: 80%
        threshold: 1%


================================================
FILE: decoder/decoder.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package decoder

/*
#cgo linux LDFLAGS: -lwebp
#cgo darwin pkg-config: libwebp
#cgo windows LDFLAGS: -lwebp
#include <stdlib.h>
#include <webp/decode.h>
*/
import "C"
import (
	"errors"
	"fmt"
	"image"
	"io"
	"unsafe"

	"github.com/kolesa-team/go-webp/utils"
)

// Decoder stores information to decode picture
type Decoder struct {
	data    []byte
	options *Options
	config  *C.WebPDecoderConfig
	dPtr    *C.uint8_t
	sPtr    C.size_t
}

// NewDecoder return new decoder instance
func NewDecoder(r io.Reader, options *Options) (d *Decoder, err error) {
	var data []byte

	if data, err = io.ReadAll(r); err != nil {
		return nil, err
	}

	if len(data) == 0 {
		return nil, errors.New("data is empty")
	}

	if options == nil {
		options = &Options{}
	}
	d = &Decoder{data: data, options: options}

	if d.config, err = d.options.GetConfig(); err != nil {
		return nil, err
	}

	d.dPtr = (*C.uint8_t)(&d.data[0])
	d.sPtr = (C.size_t)(len(d.data))

	// получаем WebPBitstreamFeatures
	if status := d.parseFeatures(d.dPtr, d.sPtr); status != utils.Vp8StatusOk {
		return nil, errors.New(fmt.Sprintf("cannot fetch features: %s", status.String()))
	}

	return
}

// Decode picture from reader
func (d *Decoder) Decode() (image.Image, error) {
	// вписываем размеры итоговой картинки
	d.config.output.width, d.config.output.height = d.getOutputDimensions()
	// указываем что декодируем в RGBA
	d.config.output.colorspace = C.MODE_RGBA
	d.config.output.is_external_memory = 1

	img := image.NewNRGBA(image.Rectangle{Max: image.Point{
		X: int(d.config.output.width),
		Y: int(d.config.output.height),
	}})

	buff := (*C.WebPRGBABuffer)(unsafe.Pointer(&d.config.output.u[0]))
	buff.stride = C.int(img.Stride)
	buff.rgba = (*C.uint8_t)(&img.Pix[0])
	buff.size = (C.size_t)(len(img.Pix))

	if status := utils.VP8StatusCode(C.WebPDecode(d.dPtr, d.sPtr, d.config)); status != utils.Vp8StatusOk {
		return nil, errors.New(fmt.Sprintf("cannot decode picture: %s", status.String()))
	}

	return img, nil
}

// GetFeatures return information about picture: width, height ...
func (d *Decoder) GetFeatures() utils.BitstreamFeatures {
	return utils.BitstreamFeatures{
		Width:        int(d.config.input.width),
		Height:       int(d.config.input.height),
		HasAlpha:     int(d.config.input.has_alpha) == 1,
		HasAnimation: int(d.config.input.has_animation) == 1,
		Format:       utils.FormatType(d.config.input.format),
	}
}

// parse features from picture
func (d *Decoder) parseFeatures(dataPtr *C.uint8_t, sizePtr C.size_t) utils.VP8StatusCode {
	return utils.VP8StatusCode(C.WebPGetFeatures(dataPtr, sizePtr, &d.config.input))
}

// return dimensions of result image
func (d *Decoder) getOutputDimensions() (width, height C.int) {
	width = d.config.input.width
	height = d.config.input.height

	if d.config.options.use_scaling > 0 {
		width = d.config.options.scaled_width
		height = d.config.options.scaled_height
	} else if d.config.options.use_cropping > 0 {
		width = d.config.options.crop_width
		height = d.config.options.crop_height
	}

	return
}


================================================
FILE: decoder/decoder_test.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package decoder

import (
	"github.com/kolesa-team/go-webp/utils"
	"github.com/stretchr/testify/require"
	"image"
	"os"
	"testing"
)

func TestNewDecoder(t *testing.T) {
	t.Run("create success", func(t *testing.T) {
		file, err := os.Open("../test_data/images/m4_q75.webp")
		if err != nil {
			t.Fatal(err)
		}

		if d, err := NewDecoder(file, &Options{
			BypassFiltering:   true,
			NoFancyUpsampling: true,
			Scale: image.Rectangle{
				Max: image.Point{
					X: 300,
					Y: 300,
				},
			},
			UseThreads:             true,
			Flip:                   true,
			DitheringStrength:      0,
			AlphaDitheringStrength: 0,
		}); err != nil {
			t.Fatal(err)
		} else if img, err := d.Decode(); err != nil {
			t.Fatal(err)
		} else if img.Bounds().Max.X <= 0 || img.Bounds().Max.Y <= 0 {
			t.Fatal("invalid decoding result")
		}
	})
	t.Run("empty file", func(t *testing.T) {
		file, err := os.Open("../test_data/images/invalid.webp")
		if err != nil {
			t.Fatal(err)
		}

		if _, err := NewDecoder(file, &Options{
			Crop: image.Rectangle{
				Max: image.Point{
					X: 150,
					Y: 150,
				},
			},
		}); err == nil {
			t.Fatal(err)
		}
	})
}

func TestDecoder_GetFeatures(t *testing.T) {
	file, err := os.Open("../test_data/images/m4_q75.webp")
	if err != nil {
		t.Fatal(err)
	}

	dec, err := NewDecoder(file, &Options{})
	if err != nil {
		t.Fatal(err)
	}

	features := dec.GetFeatures()

	if features.Width != 675 || features.Height != 900 {
		t.Fatal("incorrect dimensions")
	}

	if features.Format != utils.FormatLossy {
		t.Fatal("file format is invalid")
	}

	if features.HasAlpha {
		t.Fatal("file has_alpha is invalid")
	}

	if features.HasAlpha {
		t.Fatal("file has_animation is invalid")
	}
}

func TestDecoder_NilOptions(t *testing.T) {
	file, err := os.Open("../test_data/images/m4_q75.webp")
	require.NoError(t, err)

	_, err = NewDecoder(file, nil)
	require.NoError(t, err)
}


================================================
FILE: decoder/options.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package decoder

/*
#cgo LDFLAGS: -lwebp
#include <webp/decode.h>
*/
import "C"
import (
	"errors"
	"image"
)

// Options specifies webp decoding parameters
type Options struct {
	BypassFiltering        bool
	NoFancyUpsampling      bool
	Crop                   image.Rectangle
	Scale                  image.Rectangle
	UseThreads             bool
	Flip                   bool
	DitheringStrength      int
	AlphaDitheringStrength int
}

// GetConfig build WebPDecoderConfig for libwebp
func (o *Options) GetConfig() (*C.WebPDecoderConfig, error) {
	config := C.WebPDecoderConfig{}

	if C.WebPInitDecoderConfig(&config) == 0 {
		return nil, errors.New("cannot init decoder config")
	}

	if o.BypassFiltering {
		config.options.bypass_filtering = 1
	}

	if o.NoFancyUpsampling {
		config.options.no_fancy_upsampling = 1
	}

	// проверяем надо ли кропнуть
	if o.Crop.Max.X > 0 && o.Crop.Max.Y > 0 {
		config.options.use_cropping = 1
		config.options.crop_left = C.int(o.Crop.Min.X)
		config.options.crop_top = C.int(o.Crop.Min.Y)
		config.options.crop_width = C.int(o.Crop.Max.X)
		config.options.crop_height = C.int(o.Crop.Max.Y)
	}

	// проверяем надо ли заскейлить
	if o.Scale.Max.X > 0 && o.Scale.Max.Y > 0 {
		config.options.use_scaling = 1
		config.options.scaled_width = C.int(o.Scale.Max.X)
		config.options.scaled_height = C.int(o.Scale.Max.Y)
	}

	if o.UseThreads {
		config.options.use_threads = 1
	}

	config.options.dithering_strength = C.int(o.DitheringStrength)

	if o.Flip {
		config.options.flip = 1
	}

	config.options.alpha_dithering_strength = C.int(o.AlphaDitheringStrength)

	return &config, nil
}


================================================
FILE: decoder/options_test.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package decoder

import (
	"image"
	"testing"
)

func TestOptions_GetConfig(t *testing.T) {
	t.Run("check crop", func(t *testing.T) {
		options := &Options{
			Crop: image.Rectangle{
				Min: image.Point{X: 100, Y: 200},
				Max: image.Point{X: 400, Y: 500},
			},
		}
		if cfg, err := options.GetConfig(); err != nil {
			t.Fatal(err)
		} else if cfg.options.use_cropping != 1 {
			t.Fatal("cropping is disabled")
		} else if cfg.options.crop_left != 100 {
			t.Fatal("crop_left is invalid")
		} else if cfg.options.crop_top != 200 {
			t.Fatal("crop_top is invalid")
		} else if cfg.options.crop_width != 400 {
			t.Fatal("crop_width is invalid")
		} else if cfg.options.crop_height != 500 {
			t.Fatal("crop_height is invalid")
		}
	})
	t.Run("check scale", func(t *testing.T) {
		options := &Options{
			Scale: image.Rectangle{
				Max: image.Point{X: 400, Y: 500},
			},
		}
		if cfg, err := options.GetConfig(); err != nil {
			t.Fatal(err)
		} else if cfg.options.use_scaling != 1 {
			t.Fatal("scaling is disabled")
		} else if cfg.options.scaled_width != 400 {
			t.Fatal("scaled_width is invalid")
		} else if cfg.options.scaled_height != 500 {
			t.Fatal("scaled_height is invalid")
		}
	})
}


================================================
FILE: encoder/encoder.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package encoder

/*

#cgo linux LDFLAGS: -lwebp
#cgo darwin pkg-config: libwebp
#cgo windows LDFLAGS: -lwebp
#include <stdlib.h>
#include <webp/encode.h>
static uint8_t* encodeNRBBA(WebPConfig* config, const uint8_t* rgba, int width, int height, int stride, size_t* output_size) {
	WebPPicture pic;
	WebPMemoryWriter wrt;
	int ok;

	if (!WebPPictureInit(&pic)) {
		return NULL;
	}

	pic.use_argb = 1;
	pic.width = width;
	pic.height = height;
	pic.writer = WebPMemoryWrite;
	pic.custom_ptr = &wrt;
	WebPMemoryWriterInit(&wrt);

	ok = WebPPictureImportRGBA(&pic, rgba, stride) && WebPEncode(config, &pic);
	WebPPictureFree(&pic);

	if (!ok) {
		WebPMemoryWriterClear(&wrt);
		return NULL;
	}

	*output_size = wrt.size;
	return wrt.mem;
}
*/
import "C"
import (
	"errors"
	"image"
	"image/draw"
	"io"
	"unsafe"
)

// Encoder stores information to encode image
type Encoder struct {
	options *Options
	config  *C.WebPConfig
	img     *image.NRGBA
}

// NewEncoder return new encoder instance
func NewEncoder(src image.Image, options *Options) (e *Encoder, err error) {
	var config *C.WebPConfig

	if options == nil {
		options, _ = NewLossyEncoderOptions(PresetDefault, 75)
	}
	if config, err = options.GetConfig(); err != nil {
		return nil, err
	}

	e = &Encoder{options: options, config: config}

	switch v := src.(type) {
	case *image.NRGBA:
		e.img = v
	default:
		e.img = e.convertToNRGBA(src)
	}

	return
}

// Encode picture and flush to io.Writer
func (e *Encoder) Encode(w io.Writer) error {
	var size C.size_t

	output := C.encodeNRBBA(
		e.config,
		(*C.uint8_t)(&e.img.Pix[0]),
		C.int(e.img.Rect.Dx()),
		C.int(e.img.Rect.Dy()),
		C.int(e.img.Stride),
		&size,
	)

	if output == nil || size == 0 {
		return errors.New("cannot encode webppicture")
	}
	defer C.free(unsafe.Pointer(output))

	_, err := w.Write(((*[1 << 30]byte)(unsafe.Pointer(output)))[0:int(size):int(size)])

	return err
}

// Convert picture from any image.Image type to *image.NRGBA
// @todo optimization needed
func (e *Encoder) convertToNRGBA(src image.Image) (dst *image.NRGBA) {
	dst = image.NewNRGBA(src.Bounds())
	draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)

	return
}


================================================
FILE: encoder/encoder_test.go
================================================
package encoder

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"image"
	"os"
	"testing"
)

func TestNewEncoder(t *testing.T) {
	t.Run("encode nrgba image with lossy preset", func(t *testing.T) {
		expected := &bytes.Buffer{}
		img := image.NewNRGBA(image.Rectangle{
			Max: image.Point{
				X: 100,
				Y: 150,
			},
		})

		options, err := NewLossyEncoderOptions(PresetDefault, 0.75)
		require.NoError(t, err)

		e, err := NewEncoder(img, options)
		require.NoError(t, err)

		err = e.Encode(expected)
		require.NoError(t, err)

		actual, err := os.ReadFile("../test_data/images/100x150_lossy.webp")
		require.NoError(t, err)

		assert.Equal(t, actual, expected.Bytes())
	})
	t.Run("encode nrgba image with lossless preset", func(t *testing.T) {
		actuall := &bytes.Buffer{}
		img := image.NewNRGBA(image.Rectangle{
			Max: image.Point{
				X: 100,
				Y: 150,
			},
		})

		options, err := NewLosslessEncoderOptions(PresetDefault, 1)
		require.NoError(t, err)

		e, err := NewEncoder(img, options)
		require.NoError(t, err)

		err = e.Encode(actuall)
		require.NoError(t, err)

		expected, err := os.ReadFile("../test_data/images/100x150_lossless.webp")
		require.NoError(t, err)

		assert.Equal(t, expected, actuall.Bytes())
	})
}

func TestNewEncoder_NilOptions(t *testing.T) {
	t.Run("encode image without passing options should not panic", func(t *testing.T) {
		expected := &bytes.Buffer{}
		img := image.NewNRGBA(image.Rectangle{
			Max: image.Point{
				X: 100,
				Y: 150,
			},
		})

		e, err := NewEncoder(img, nil)
		require.NoError(t, err)

		err = e.Encode(expected)
		require.NoError(t, err)
	})
}


================================================
FILE: encoder/options.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package encoder

/*
#cgo LDFLAGS: -lwebp
#include <webp/encode.h>
*/
import "C"
import (
	"errors"
	"fmt"
)

// Default libwebp image hints
//noinspection GoUnusedConst
const (
	HintDefault ImageHint = iota
	HintPicture
	HintPhoto
	HintGraph
	HintLast
)

// Default libwebp presets
//noinspection GoUnusedConst
const (
	PresetDefault EncodingPreset = iota
	PresetPicture
	PresetPhoto
	PresetDrawing
	PresetIcon
	PresetText
)

type (
	// ImageHint hint of picture
	ImageHint int
	// EncodingPreset using Preset
	EncodingPreset int
	// Options specifies webp encoding parameters
	Options struct {
		config *C.WebPConfig

		Lossless         bool
		Quality          float32
		Method           int
		ImageHint        ImageHint
		TargetSize       int
		TargetPsnr       float32
		Segments         int
		SnsStrength      int
		FilterStrength   int
		FilterSharpness  int
		FilterType       int
		Autofilter       bool
		AlphaCompression int
		AlphaFiltering   int
		alphaQuality     int
		Pass             int
		// Disabled for compatibility with old version libwebp
		// QMin             int
		// QMax             int
		ShowCompressed  bool
		Preprocessing   int
		Partitions      int
		PartitionLimit  int
		EmulateJpegSize bool
		ThreadLevel     bool
		LowMemory       bool
		NearLossless    int
		Exact           int
		UseDeltaPalette bool
		UseSharpYuv     bool
	}
)

// NewLossyEncoderOptions build lossy encoding options
func NewLossyEncoderOptions(preset EncodingPreset, quality float32) (options *Options, err error) {
	options = &Options{
		config: &C.WebPConfig{},
	}

	if C.WebPConfigPreset(options.config, C.WebPPreset(preset), C.float(quality)) == 0 {
		return nil, errors.New("cannot init encoder config")
	}

	options.sync()

	return
}

// NewLosslessEncoderOptions build lossless encoding options
func NewLosslessEncoderOptions(preset EncodingPreset, level int) (options *Options, err error) {
	if options, err = NewLossyEncoderOptions(preset, 0); err != nil {
		return
	}
	if C.WebPConfigLosslessPreset(options.config, C.int(level)) == 0 {
		return nil, errors.New("cannot init lossless preset")
	}

	options.sync()

	return
}

func (o *Options) sync() {
	o.Lossless = o.config.lossless == 1
	o.Quality = float32(o.config.quality)
	o.Method = int(o.config.method)
	o.ImageHint = ImageHint(o.config.image_hint)
	o.TargetSize = int(o.config.target_size)
	o.TargetPsnr = float32(o.config.target_PSNR)
	o.Segments = int(o.config.segments)
	o.SnsStrength = int(o.config.sns_strength)
	o.FilterStrength = int(o.config.filter_strength)
	o.FilterSharpness = int(o.config.filter_sharpness)
	o.FilterType = int(o.config.filter_type)
	o.Autofilter = o.config.autofilter == 1
	o.AlphaCompression = int(o.config.alpha_compression)
	o.AlphaFiltering = int(o.config.alpha_filtering)
	o.alphaQuality = int(o.config.alpha_quality)
	o.Pass = int(o.config.pass)
	// Disabled for compatibility with old version libwebp
	// o.QMin = int(o.config.qmin)
	// o.QMax = int(o.config.qmax)
	o.ShowCompressed = o.config.show_compressed == 1
	o.Preprocessing = int(o.config.preprocessing)
	o.Partitions = int(o.config.partitions)
	o.PartitionLimit = int(o.config.partition_limit)
	o.EmulateJpegSize = o.config.emulate_jpeg_size == 1
	o.ThreadLevel = o.config.thread_level == 1
	o.LowMemory = o.config.low_memory == 1
	o.NearLossless = int(o.config.near_lossless)
	o.Exact = int(o.config.exact)
	o.UseDeltaPalette = o.config.use_delta_palette == 1
	o.UseSharpYuv = o.config.use_sharp_yuv == 1
}

func (o *Options) boolToCInt(expression bool) (result C.int) {
	result = 0

	if expression {
		result = 1
	}

	return
}

// GetConfig build WebPConfig for libwebp
func (o *Options) GetConfig() (*C.WebPConfig, error) {
	var err error
	if o == nil {
		o, err = NewLosslessEncoderOptions(PresetDefault, 0)
		if err != nil {
			return nil, fmt.Errorf("cannot validate default config: [%w]", err)
		}
	}
	o.config.lossless = o.boolToCInt(o.Lossless)
	o.config.quality = C.float(o.Quality)
	o.config.method = C.int(o.Method)
	o.config.image_hint = C.WebPImageHint(o.ImageHint)
	o.config.target_size = C.int(o.TargetSize)
	o.config.target_PSNR = C.float(o.TargetPsnr)
	o.config.segments = C.int(o.Segments)
	o.config.sns_strength = C.int(o.SnsStrength)
	o.config.filter_strength = C.int(o.FilterStrength)
	o.config.filter_sharpness = C.int(o.FilterSharpness)
	o.config.filter_type = C.int(o.FilterType)
	o.config.autofilter = o.boolToCInt(o.Autofilter)
	o.config.alpha_compression = C.int(o.AlphaCompression)
	o.config.alpha_filtering = C.int(o.AlphaFiltering)
	o.config.alpha_quality = C.int(o.alphaQuality)
	o.config.pass = C.int(o.Pass)
	// Disabled for compatibility with old version libwebp
	// o.config.qmin = C.int(o.QMin)
	// o.config.qmax = C.int(o.QMax)
	o.config.show_compressed = o.boolToCInt(o.ShowCompressed)
	o.config.preprocessing = C.int(o.Preprocessing)
	o.config.partitions = C.int(o.Partitions)
	o.config.partition_limit = C.int(o.PartitionLimit)
	o.config.emulate_jpeg_size = o.boolToCInt(o.EmulateJpegSize)
	o.config.thread_level = o.boolToCInt(o.ThreadLevel)
	o.config.low_memory = o.boolToCInt(o.LowMemory)
	o.config.near_lossless = C.int(o.NearLossless)
	o.config.exact = C.int(o.Exact)
	o.config.use_delta_palette = o.boolToCInt(o.UseDeltaPalette)
	o.config.use_sharp_yuv = o.boolToCInt(o.UseSharpYuv)

	if C.WebPValidateConfig(o.config) == 0 {
		return nil, errors.New("cannot validate config")
	}

	return o.config, nil
}


================================================
FILE: encoder/options_test.go
================================================
package encoder

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"testing"
)

func TestNilOptions(t *testing.T) {
	t.Run("config from nil Options succeeds", func(t *testing.T) {
		var options *Options = nil
		cfg, err := options.GetConfig()
		require.NoError(t, err)
		require.NotNil(t, cfg)
	})
}

func TestNewLossyEncoderOptions(t *testing.T) {
	t.Run("invalid quality", func(t *testing.T) {
		options, err := NewLossyEncoderOptions(PresetDefault, -1)
		require.Error(t, err)
		assert.Nil(t, options)
	})
	t.Run("create lossy encoder options is success", func(t *testing.T) {
		options, err := NewLossyEncoderOptions(PresetDefault, 75)
		require.Nil(t, err)
		require.NotNil(t, options)

		assert.False(t, options.Lossless)
		assert.Equal(t, float32(75), options.Quality)

		cfg, err := options.GetConfig()
		require.NoError(t, err)
		assert.NotNil(t, cfg)
	})
}

func TestNewLosslessEncoderOptions(t *testing.T) {
	t.Run("invalid level of lossless encoder", func(t *testing.T) {
		options, err := NewLosslessEncoderOptions(PresetDefault, -1)
		require.Error(t, err)
		assert.Nil(t, options)
	})
	t.Run("create lossless encoder options is success", func(t *testing.T) {
		options, err := NewLosslessEncoderOptions(PresetDefault, 1)
		require.Nil(t, err)
		require.NotNil(t, options)

		assert.True(t, options.Lossless)
		assert.Equal(t, 1, options.Method)
		assert.Equal(t, float32(20), options.Quality)

		cfg, err := options.GetConfig()
		require.NoError(t, err)
		assert.NotNil(t, cfg)
	})
}


================================================
FILE: go.mod
================================================
module github.com/kolesa-team/go-webp

go 1.10

require github.com/stretchr/testify v1.7.0


================================================
FILE: go.sum
================================================
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: utils/vp8.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package utils

//noinspection GoUnusedConst
const (
	Vp8StatusOk VP8StatusCode = iota
	Vp8StatusOutOfMemory
	Vp8StatusInvalidParam
	Vp8StatusBitstreamError
	Vp8StatusUnsupportedFeature
	Vp8StatusSuspended
	Vp8StatusUserAbort
	Vp8StatusNotEnoughData
)

type VP8StatusCode int

func (c VP8StatusCode) String() (label string) {
	switch c {
	case Vp8StatusOk:
		label = "VP8_STATUS_OK"
	case Vp8StatusOutOfMemory:
		label = "VP8_STATUS_OUT_OF_MEMORY"
	case Vp8StatusInvalidParam:
		label = "VP8_STATUS_INVALID_PARAM"
	case Vp8StatusBitstreamError:
		label = "VP8_STATUS_BITSTREAM_ERROR"
	case Vp8StatusUnsupportedFeature:
		label = "VP8_STATUS_UNSUPPORTED_FEATURE"
	case Vp8StatusSuspended:
		label = "VP8_STATUS_SUSPENDED"
	case Vp8StatusUserAbort:
		label = "VP8_STATUS_USER_ABORT"
	case Vp8StatusNotEnoughData:
		label = "VP8_STATUS_NOT_ENOUGH_DATA"
	default:
		label = "VP8 undefined status code"
	}

	return
}

const (
	Vp8EncOk Vp8EncStatus = iota
	Vp8EncErrorOutOfMemory
	Vp8EncErrorBitstreamOutOfMemory
	Vp8EncErrorNullParameter
	Vp8EncErrorInvalidConfiguration
	Vp8EncErrorBadDimension
	Vp8EncErrorPartition0Overflow
	Vp8EncErrorPartitionOverflow
	Vp8EncErrorBadWrite
	Vp8EncErrorFileTooBig
	Vp8EncErrorUserAbort
	Vp8EncErrorLast
)

type Vp8EncStatus int

func (c Vp8EncStatus) String() (label string) {
	switch c {
	case Vp8EncOk:
		label = "VP8_ENC_OK"
	case Vp8EncErrorOutOfMemory:
		label = "VP8_ENC_ERROR_OUT_OF_MEMORY"
	case Vp8EncErrorBitstreamOutOfMemory:
		label = "VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY"
	case Vp8EncErrorNullParameter:
		label = "VP8_ENC_ERROR_NULL_PARAMETER"
	case Vp8EncErrorInvalidConfiguration:
		label = "VP8_ENC_ERROR_INVALID_CONFIGURATION"
	case Vp8EncErrorBadDimension:
		label = "VP8_ENC_ERROR_BAD_DIMENSION"
	case Vp8EncErrorPartition0Overflow:
		label = "VP8_ENC_ERROR_PARTITION0_OVERFLOW"
	case Vp8EncErrorPartitionOverflow:
		label = "VP8_ENC_ERROR_PARTITION_OVERFLOW"
	case Vp8EncErrorBadWrite:
		label = "VP8_ENC_ERROR_BAD_WRITE"
	case Vp8EncErrorFileTooBig:
		label = "VP8_ENC_ERROR_FILE_TOO_BIG"
	case Vp8EncErrorUserAbort:
		label = "VP8_ENC_ERROR_USER_ABORT"
	case Vp8EncErrorLast:
		label = "VP8_ENC_ERROR_LAST"
	default:
		label = "VP8 undefined status code"
	}

	return
}


================================================
FILE: utils/webp.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package utils

type FormatType int

//noinspection GoUnusedConst
const (
	FormatUndefined FormatType = iota
	FormatLossy
	FormatLossless
)

type BitstreamFeatures struct {
	Width        int
	Height       int
	HasAlpha     bool
	HasAnimation bool
	Format       FormatType
}


================================================
FILE: webp/webp.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package webp

import (
	"github.com/kolesa-team/go-webp/decoder"
	"github.com/kolesa-team/go-webp/encoder"
	"image"
	"image/color"
	"io"
)

func init() {
	image.RegisterFormat("webp", "RIFF????WEBPVP8", quickDecode, quickDecodeConfig)
}

func quickDecode(r io.Reader) (image.Image, error) {
	return Decode(r, &decoder.Options{})
}

func quickDecodeConfig(r io.Reader) (image.Config, error) {
	return DecodeConfig(r, &decoder.Options{})
}

// Decode picture from reader
func Decode(r io.Reader, options *decoder.Options) (image.Image, error) {
	if dec, err := decoder.NewDecoder(r, options); err != nil {
		return nil, err
	} else {
		return dec.Decode()
	}
}

func DecodeConfig(r io.Reader, options *decoder.Options) (image.Config, error) {
	if dec, err := decoder.NewDecoder(r, options); err != nil {
		return image.Config{}, err
	} else {
		return image.Config{
			ColorModel: color.RGBAModel,
			Width:      dec.GetFeatures().Width,
			Height:     dec.GetFeatures().Height,
		}, nil
	}
}

// Encode picture and write to io.Writer
func Encode(w io.Writer, src image.Image, options *encoder.Options) error {
	if enc, err := encoder.NewEncoder(src, options); err != nil {
		return err
	} else {
		return enc.Encode(w)
	}
}


================================================
FILE: webp/webp_test.go
================================================
// The MIT License (MIT)
//
// Copyright (c) 2019 Amangeldy Kadyl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package webp

import (
	"bytes"
	"github.com/kolesa-team/go-webp/decoder"
	"github.com/kolesa-team/go-webp/encoder"
	"github.com/stretchr/testify/require"
	"image"
	"image/color"
	"image/jpeg"
	"io"
	"os"
	"testing"
)

func TestEncode(t *testing.T) {
	t.Run("encode lossy", func(t *testing.T) {
		r, err := os.Open("../test_data/images/source.jpg")
		if err != nil {
			t.Fatal(err)
		}

		img, err := jpeg.Decode(r)
		if err != nil {
			t.Fatal(err)
		}

		options, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)
		if err != nil {
			t.Fatal(err)
		}

		if err = Encode(io.Discard, img, options); err != nil {
			t.Fatal(err)
		}
	})
	t.Run("encode lossless", func(t *testing.T) {
		r, err := os.Open("../test_data/images/source.jpg")
		if err != nil {
			t.Fatal(err)
		}

		img, err := jpeg.Decode(r)
		if err != nil {
			t.Fatal(err)
		}

		options, err := encoder.NewLosslessEncoderOptions(encoder.PresetDefault, 4)
		if err != nil {
			t.Fatal(err)
		}

		if err = Encode(io.Discard, img, options); err != nil {
			t.Fatal(err)
		}
	})
	t.Run("failed encoding with large size", func(t *testing.T) {
		img := image.NewNRGBA(image.Rectangle{
			Max: image.Point{X: 20000, Y: 1},
		})

		options, err := encoder.NewLosslessEncoderOptions(encoder.PresetDefault, 4)
		if err != nil {
			t.Fatal(err)
		}

		if err = Encode(io.Discard, img, options); err == nil {
			t.Fatal("an error was expected")
		}
	})
}

func TestDecode(t *testing.T) {
	t.Run("decode lossy webp picture with many options", func(t *testing.T) {
		r, err := os.Open("../test_data/images/webp-logo-lossy.webp")
		if err != nil {
			t.Fatal(err)
		}

		if img, err := Decode(r, &decoder.Options{
			BypassFiltering:   true,
			NoFancyUpsampling: true,

			Crop: image.Rectangle{},
			Scale: image.Rectangle{
				Max: image.Point{
					X: 400,
					Y: 300,
				},
			},

			UseThreads:             true,
			Flip:                   true,
			DitheringStrength:      1,
			AlphaDitheringStrength: 1,
		}); err != nil {
			t.Fatal(err)
		} else if img == nil {
			t.Fatal("img is empty")
		} else if img.Bounds().Max.X != 400 || img.Bounds().Max.Y != 300 {
			t.Fatal("img is invalid")
		}
	})
	t.Run("decode lossless webp picture", func(t *testing.T) {
		r, err := os.Open("../test_data/images/webp-logo-lossless.webp")
		if err != nil {
			t.Fatal(err)
		}

		if img, err := Decode(r, &decoder.Options{}); err != nil {
			t.Fatal(err)
		} else if img == nil {
			t.Fatal("img is empty")
		}
	})
	t.Run("decode invalid webp file", func(t *testing.T) {
		r, err := os.Open("../test_data/images/invalid.webp")
		if err != nil {
			t.Fatal(err)
		}

		if _, err := Decode(r, &decoder.Options{}); err == nil {
			t.Fatal("an error was expected")
		}
	})
	t.Run("decode lossy image via standard image.Decode", func(t *testing.T) {
		r, err := os.Open("../test_data/images/webp-logo-lossy.webp")
		require.NoError(t, err)
		img, format, err := image.Decode(r)
		require.NoError(t, err)

		require.Equal(t, "webp", format)
		require.Equal(t, color.NRGBAModel, img.ColorModel())
	})
	t.Run("decode lossless image via standard image.Decode", func(t *testing.T) {
		r, err := os.Open("../test_data/images/webp-logo-lossless.webp")
		require.NoError(t, err)
		img, format, err := image.Decode(r)
		require.NoError(t, err)

		require.Equal(t, "webp", format)
		require.Equal(t, color.NRGBAModel, img.ColorModel())
	})
}

func BenchmarkDecodeLossy(b *testing.B) {
	data, err := os.ReadFile("../test_data/images/webp-logo-lossy.webp")
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		dec, err := decoder.NewDecoder(bytes.NewBuffer(data), &decoder.Options{})
		if err != nil {
			b.Fatal(err)
		}

		if _, err := dec.Decode(); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkDecodeXImageLossy(b *testing.B) {
	data, err := os.ReadFile("../test_data/images/webp-logo-lossy.webp")
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		_, err = Decode(bytes.NewBuffer(data), &decoder.Options{})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkDecodeLossless(b *testing.B) {
	data, err := os.ReadFile("../test_data/images/webp-logo-lossless.webp")
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		dec, err := decoder.NewDecoder(bytes.NewBuffer(data), &decoder.Options{})
		if err != nil {
			b.Fatal(err)
		}

		if _, err := dec.Decode(); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkDecodeXImageLossless(b *testing.B) {
	data, err := os.ReadFile("../test_data/images/webp-logo-lossless.webp")
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		_, err = Decode(bytes.NewBuffer(data), &decoder.Options{})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkEncode(b *testing.B) {
	r, err := os.Open("../test_data/images/source.jpg")
	if err != nil {
		b.Fatal(err)
	}

	img, err := jpeg.Decode(r)
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		options, err := encoder.NewLossyEncoderOptions(encoder.PresetDefault, 75)
		if err != nil {
			b.Fatal(err)
		}

		if err = Encode(io.Discard, img, options); err != nil {
			b.Fatal(err)
		}
	}
}
Download .txt
gitextract_hax6w2m8/

├── .gitignore
├── .travis.yml
├── AUTHORS
├── LICENSE
├── Makefile
├── README.md
├── _examples/
│   ├── decode/
│   │   └── main.go
│   └── encode/
│       └── main.go
├── codecov.yml
├── decoder/
│   ├── decoder.go
│   ├── decoder_test.go
│   ├── options.go
│   └── options_test.go
├── encoder/
│   ├── encoder.go
│   ├── encoder_test.go
│   ├── options.go
│   └── options_test.go
├── go.mod
├── go.sum
├── utils/
│   ├── vp8.go
│   └── webp.go
└── webp/
    ├── webp.go
    └── webp_test.go
Download .txt
SYMBOL INDEX (84 symbols across 14 files)

FILE: _examples/decode/main.go
  function main (line 33) | func main() {

FILE: _examples/encode/main.go
  function main (line 32) | func main() {

FILE: decoder/decoder.go
  type Decoder (line 43) | type Decoder struct
    method Decode (line 84) | func (d *Decoder) Decode() (image.Image, error) {
    method GetFeatures (line 109) | func (d *Decoder) GetFeatures() utils.BitstreamFeatures {
    method parseFeatures (line 120) | func (d *Decoder) parseFeatures(dataPtr *C.uint8_t, sizePtr C.size_t) ...
    method getOutputDimensions (line 125) | func (d *Decoder) getOutputDimensions() (width, height C.int) {
  function NewDecoder (line 52) | func NewDecoder(r io.Reader, options *Options) (d *Decoder, err error) {

FILE: decoder/decoder_test.go
  function TestNewDecoder (line 32) | func TestNewDecoder(t *testing.T) {
  function TestDecoder_GetFeatures (line 79) | func TestDecoder_GetFeatures(t *testing.T) {
  function TestDecoder_NilOptions (line 109) | func TestDecoder_NilOptions(t *testing.T) {

FILE: decoder/options.go
  type Options (line 35) | type Options struct
    method GetConfig (line 47) | func (o *Options) GetConfig() (*C.WebPDecoderConfig, error) {

FILE: decoder/options_test.go
  function TestOptions_GetConfig (line 29) | func TestOptions_GetConfig(t *testing.T) {

FILE: encoder/encoder.go
  type Encoder (line 69) | type Encoder struct
    method Encode (line 99) | func (e *Encoder) Encode(w io.Writer) error {
    method convertToNRGBA (line 123) | func (e *Encoder) convertToNRGBA(src image.Image) (dst *image.NRGBA) {
  function NewEncoder (line 76) | func NewEncoder(src image.Image, options *Options) (e *Encoder, err erro...

FILE: encoder/encoder_test.go
  function TestNewEncoder (line 12) | func TestNewEncoder(t *testing.T) {
  function TestNewEncoder_NilOptions (line 61) | func TestNewEncoder_NilOptions(t *testing.T) {

FILE: encoder/options.go
  constant HintDefault (line 37) | HintDefault ImageHint = iota
  constant HintPicture (line 38) | HintPicture
  constant HintPhoto (line 39) | HintPhoto
  constant HintGraph (line 40) | HintGraph
  constant HintLast (line 41) | HintLast
  constant PresetDefault (line 47) | PresetDefault EncodingPreset = iota
  constant PresetPicture (line 48) | PresetPicture
  constant PresetPhoto (line 49) | PresetPhoto
  constant PresetDrawing (line 50) | PresetDrawing
  constant PresetIcon (line 51) | PresetIcon
  constant PresetText (line 52) | PresetText
  type ImageHint (line 57) | type ImageHint
  type EncodingPreset (line 59) | type EncodingPreset
  type Options (line 61) | type Options struct
    method sync (line 126) | func (o *Options) sync() {
    method boolToCInt (line 159) | func (o *Options) boolToCInt(expression bool) (result C.int) {
    method GetConfig (line 170) | func (o *Options) GetConfig() (*C.WebPConfig, error) {
  function NewLossyEncoderOptions (line 98) | func NewLossyEncoderOptions(preset EncodingPreset, quality float32) (opt...
  function NewLosslessEncoderOptions (line 113) | func NewLosslessEncoderOptions(preset EncodingPreset, level int) (option...

FILE: encoder/options_test.go
  function TestNilOptions (line 9) | func TestNilOptions(t *testing.T) {
  function TestNewLossyEncoderOptions (line 18) | func TestNewLossyEncoderOptions(t *testing.T) {
  function TestNewLosslessEncoderOptions (line 38) | func TestNewLosslessEncoderOptions(t *testing.T) {

FILE: utils/vp8.go
  constant Vp8StatusOk (line 26) | Vp8StatusOk VP8StatusCode = iota
  constant Vp8StatusOutOfMemory (line 27) | Vp8StatusOutOfMemory
  constant Vp8StatusInvalidParam (line 28) | Vp8StatusInvalidParam
  constant Vp8StatusBitstreamError (line 29) | Vp8StatusBitstreamError
  constant Vp8StatusUnsupportedFeature (line 30) | Vp8StatusUnsupportedFeature
  constant Vp8StatusSuspended (line 31) | Vp8StatusSuspended
  constant Vp8StatusUserAbort (line 32) | Vp8StatusUserAbort
  constant Vp8StatusNotEnoughData (line 33) | Vp8StatusNotEnoughData
  type VP8StatusCode (line 36) | type VP8StatusCode
    method String (line 38) | func (c VP8StatusCode) String() (label string) {
  constant Vp8EncOk (line 64) | Vp8EncOk Vp8EncStatus = iota
  constant Vp8EncErrorOutOfMemory (line 65) | Vp8EncErrorOutOfMemory
  constant Vp8EncErrorBitstreamOutOfMemory (line 66) | Vp8EncErrorBitstreamOutOfMemory
  constant Vp8EncErrorNullParameter (line 67) | Vp8EncErrorNullParameter
  constant Vp8EncErrorInvalidConfiguration (line 68) | Vp8EncErrorInvalidConfiguration
  constant Vp8EncErrorBadDimension (line 69) | Vp8EncErrorBadDimension
  constant Vp8EncErrorPartition0Overflow (line 70) | Vp8EncErrorPartition0Overflow
  constant Vp8EncErrorPartitionOverflow (line 71) | Vp8EncErrorPartitionOverflow
  constant Vp8EncErrorBadWrite (line 72) | Vp8EncErrorBadWrite
  constant Vp8EncErrorFileTooBig (line 73) | Vp8EncErrorFileTooBig
  constant Vp8EncErrorUserAbort (line 74) | Vp8EncErrorUserAbort
  constant Vp8EncErrorLast (line 75) | Vp8EncErrorLast
  type Vp8EncStatus (line 78) | type Vp8EncStatus
    method String (line 80) | func (c Vp8EncStatus) String() (label string) {

FILE: utils/webp.go
  type FormatType (line 24) | type FormatType
  constant FormatUndefined (line 28) | FormatUndefined FormatType = iota
  constant FormatLossy (line 29) | FormatLossy
  constant FormatLossless (line 30) | FormatLossless
  type BitstreamFeatures (line 33) | type BitstreamFeatures struct

FILE: webp/webp.go
  function init (line 32) | func init() {
  function quickDecode (line 36) | func quickDecode(r io.Reader) (image.Image, error) {
  function quickDecodeConfig (line 40) | func quickDecodeConfig(r io.Reader) (image.Config, error) {
  function Decode (line 45) | func Decode(r io.Reader, options *decoder.Options) (image.Image, error) {
  function DecodeConfig (line 53) | func DecodeConfig(r io.Reader, options *decoder.Options) (image.Config, ...
  function Encode (line 66) | func Encode(w io.Writer, src image.Image, options *encoder.Options) error {

FILE: webp/webp_test.go
  function TestEncode (line 37) | func TestEncode(t *testing.T) {
  function TestDecode (line 94) | func TestDecode(t *testing.T) {
  function BenchmarkDecodeLossy (line 167) | func BenchmarkDecodeLossy(b *testing.B) {
  function BenchmarkDecodeXImageLossy (line 185) | func BenchmarkDecodeXImageLossy(b *testing.B) {
  function BenchmarkDecodeLossless (line 199) | func BenchmarkDecodeLossless(b *testing.B) {
  function BenchmarkDecodeXImageLossless (line 217) | func BenchmarkDecodeXImageLossless(b *testing.B) {
  function BenchmarkEncode (line 231) | func BenchmarkEncode(b *testing.B) {
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (53K chars).
[
  {
    "path": ".gitignore",
    "chars": 42,
    "preview": "/.idea\n/vendor\n.DS_Store\n/example/output*\n"
  },
  {
    "path": ".travis.yml",
    "chars": 345,
    "preview": "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 "
  },
  {
    "path": "AUTHORS",
    "chars": 80,
    "preview": "Google LLC (https://opensource.google.com/)\nAmangeldy Kadyl <lex0.kz@gmail.com>\n"
  },
  {
    "path": "LICENSE",
    "chars": 1082,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2019 Amangeldy Kadyl\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "Makefile",
    "chars": 232,
    "preview": ".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"
  },
  {
    "path": "README.md",
    "chars": 2948,
    "preview": "![logo](test_data/images/logo.jpg)\n# go-webp\n[![Build Status](https://api.travis-ci.com/kolesa-team/go-webp.svg?branch=m"
  },
  {
    "path": "_examples/decode/main.go",
    "chars": 1728,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "_examples/encode/main.go",
    "chars": 1804,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "codecov.yml",
    "chars": 142,
    "preview": "ignore:\n  - \"_examples\"\n  - \"utils\"\n  - \"test_data\"\ncoverage:\n  status:\n    project:\n      default:\n        target: 80%\n"
  },
  {
    "path": "decoder/decoder.go",
    "chars": 4175,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "decoder/decoder_test.go",
    "chars": 3039,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "decoder/options.go",
    "chars": 2753,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "decoder/options_test.go",
    "chars": 2343,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "encoder/encoder.go",
    "chars": 3311,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "encoder/encoder_test.go",
    "chars": 1664,
    "preview": "package encoder\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"image\""
  },
  {
    "path": "encoder/options.go",
    "chars": 6559,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "encoder/options_test.go",
    "chars": 1536,
    "preview": "package encoder\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"testing\"\n)\n\nfun"
  },
  {
    "path": "go.mod",
    "chars": 91,
    "preview": "module github.com/kolesa-team/go-webp\n\ngo 1.10\n\nrequire github.com/stretchr/testify v1.7.0\n"
  },
  {
    "path": "go.sum",
    "chars": 1024,
    "preview": "github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=\ngithub.com/davecgh/go-spew v1.1.0/go.m"
  },
  {
    "path": "utils/vp8.go",
    "chars": 3365,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "utils/webp.go",
    "chars": 1412,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "webp/webp.go",
    "chars": 2362,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  },
  {
    "path": "webp/webp_test.go",
    "chars": 6252,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2019 Amangeldy Kadyl\n//\n// Permission is hereby granted, free of charge, to"
  }
]

About this extraction

This page contains the full source code of the kolesa-team/go-webp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (47.2 KB), approximately 13.6k tokens, and a symbol index with 84 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!