Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb400ed6ae | |||
| 143c335818 | |||
| 161914c34c | |||
| 92e0c58b22 | |||
| beaa2dc5fe | |||
| 136f840905 | |||
| e2272bfa0a | |||
| 6cfd09fce0 | |||
| a91911f351 | |||
| 55dba76952 | |||
| ac3f1de391 | |||
| 2dcf1dbc15 | |||
| ae7119f65a | |||
| 774de75d6f | |||
| 1659609ae1 | |||
| dcd3061482 | |||
| 448be1bb98 | |||
| bc4eb13058 | |||
| 1a7a435460 | |||
| c16736d86f | |||
| 9a833d025a | |||
| 525dcf31ad | |||
| ebb5f3cc7a | |||
| ad30f36365 | |||
| 020082d830 | |||
| 03b7fcdd89 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cmd/mkthumb/mkthumb
|
||||||
|
cmd/mkthumb/mkthumb.exe
|
||||||
5
.hgtags
5
.hgtags
@@ -1,5 +0,0 @@
|
|||||||
efd7b407177086c57e8c086605c2c8d1cee23840 release-1.0
|
|
||||||
292439a79182796c2f6277ef13ca179379b0fb88 v0.2.0
|
|
||||||
efd7b407177086c57e8c086605c2c8d1cee23840 v0.1.0
|
|
||||||
efd7b407177086c57e8c086605c2c8d1cee23840 release-1.0
|
|
||||||
0000000000000000000000000000000000000000 release-1.0
|
|
||||||
56
CachingThumbnailer.go
Normal file
56
CachingThumbnailer.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mime"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CachingThumbnailer struct {
|
||||||
|
t Thumbnailer
|
||||||
|
cache *lru.Cache // threadsafe, might be nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Thumbnailer = &CachingThumbnailer{} // interface assertion
|
||||||
|
|
||||||
|
func NewCachingThumbnailer(cacheSize int, opts *Config) (Thumbnailer, error) {
|
||||||
|
|
||||||
|
upstream := NewThumbnailerEx(opts)
|
||||||
|
|
||||||
|
if cacheSize == 0 {
|
||||||
|
return upstream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbCache, err := lru.New(cacheSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CachingThumbnailer{
|
||||||
|
t: upstream,
|
||||||
|
cache: thumbCache,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CachingThumbnailer) RenderFile(absPath string) ([]byte, error) {
|
||||||
|
mimeType := mime.TypeByExtension(filepath.Ext(absPath))
|
||||||
|
return this.RenderFileAs(absPath, mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CachingThumbnailer) RenderFileAs(absPath, mimeType string) ([]byte, error) {
|
||||||
|
thumb, ok := this.cache.Get(absPath)
|
||||||
|
if ok {
|
||||||
|
return thumb.([]byte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to cache
|
||||||
|
rendered, err := this.t.RenderFile(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache.Add(absPath, rendered)
|
||||||
|
|
||||||
|
return rendered, nil
|
||||||
|
}
|
||||||
67
DirectThumbnailer.go
Normal file
67
DirectThumbnailer.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"mime"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DirectThumbnailer struct {
|
||||||
|
cfg Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Thumbnailer = &DirectThumbnailer{} // interface assertion
|
||||||
|
|
||||||
|
func NewThumbnailer(w, h int) Thumbnailer {
|
||||||
|
opts := DefaultConfig
|
||||||
|
opts.Width = w
|
||||||
|
opts.Height = h
|
||||||
|
|
||||||
|
return NewThumbnailerEx(&opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewThumbnailerEx(opts *Config) Thumbnailer {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &DefaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DirectThumbnailer{cfg: *opts}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) RenderFile(absPath string) ([]byte, error) {
|
||||||
|
mimeType := mime.TypeByExtension(filepath.Ext(absPath))
|
||||||
|
return this.RenderFileAs(absPath, mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) RenderFileAs(absPath, mimeType string) ([]byte, error) {
|
||||||
|
|
||||||
|
// Decode source
|
||||||
|
|
||||||
|
var src image.Image
|
||||||
|
var err error = nil
|
||||||
|
if strings.HasPrefix(mimeType, `image/`) {
|
||||||
|
src, err = this.imageSnapshot(absPath, mimeType)
|
||||||
|
|
||||||
|
} else if strings.HasPrefix(mimeType, `video/`) {
|
||||||
|
src, err = this.videoSnapshot(absPath)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return nil, ErrUnsupportedFiletype
|
||||||
|
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
|
||||||
|
dest, err := this.scaleImage(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rasterise result
|
||||||
|
|
||||||
|
return this.encode(dest)
|
||||||
|
}
|
||||||
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# thumbnail
|
||||||
|
|
||||||
|
A thumbnailing library for Go.
|
||||||
|
|
||||||
|
- Supports jpeg / png / gif / bmp / webp files (internally)
|
||||||
|
- Supports video files (requires `ffmpeg` in `$PATH`)
|
||||||
|
- Optional LRU cache of recent thumbnails for performance
|
||||||
|
- Sampling algorithms: Nearest-neighbour, Bilinear (fast), Bilinear (accurate), Bicubic (Catmull-Rom)
|
||||||
|
- Scaling algorithms: Fit inside, Fit outside, Stretch
|
||||||
|
- Output formats: JPG, PNG, Quantised/lossy PNG (via `go-imagequant`), BMP
|
||||||
|
|
||||||
|
A standalone binary `mkthumb` is provided as a sample utility.
|
||||||
|
|
||||||
|
This package can be installed via go get: `go get code.ivysaur.me/thumbnail`
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
2018-12-31 1.0.1
|
||||||
|
- Convert to Go Modules
|
||||||
|
- Update vendored dependencies
|
||||||
|
|
||||||
|
2018-06-09 1.0.0
|
||||||
|
- Feature: Major speed improvement
|
||||||
|
- Feature: Support FitInside, FitOutside, and Stretch stretching modes
|
||||||
|
- Feature: Support Bilnear (Fast), Bilnear (Accurate), and Bicubic (Catmull-Rom) scaling algorithms
|
||||||
|
- Feature: Support PNG output without imagequant; support BMP output
|
||||||
|
- Feature: Support Webp and BMP input files
|
||||||
|
- Fix wrong dimension output for video input files
|
||||||
|
- Fix jagged output of bilinear resizer
|
||||||
|
|
||||||
|
2018-06-04 0.2.1
|
||||||
|
- Add `disableimagecrush` build tag option to exclude cgo library dependency
|
||||||
|
|
||||||
|
2017-11-18 0.2.0
|
||||||
|
- Initial standalone release
|
||||||
|
- Feature: Decode input with specified mime type
|
||||||
|
- Feature: Allow passing zero as thumbnail cache size
|
||||||
|
|
||||||
|
2017-01-03 0.1.0
|
||||||
|
- Version of `thumbnail` vendored with `webdir` 1.0 (previously tagged as `release-1.0`)
|
||||||
145
Thumbnailer.go
145
Thumbnailer.go
@@ -2,34 +2,7 @@ package thumbnail
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"image/gif"
|
|
||||||
"image/jpeg"
|
|
||||||
"image/png"
|
|
||||||
"mime"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OutputFormat uint8
|
|
||||||
type AspectFormat uint8
|
|
||||||
type ScaleFormat uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
OUTPUT_PNG_CRUSH OutputFormat = 2
|
|
||||||
OUTPUT_JPG OutputFormat = 3
|
|
||||||
OUTPUT__DEFAULT = OUTPUT_PNG_CRUSH
|
|
||||||
|
|
||||||
ASPECT_PAD_TO_DIMENSIONS AspectFormat = 80
|
|
||||||
ASPECT_RESPECT_MAX_DIMENSION_ONLY AspectFormat = 81
|
|
||||||
ASPECT_CROP_TO_DIMENSIONS AspectFormat = 82
|
|
||||||
ASPECT__DEFAULT = ASPECT_PAD_TO_DIMENSIONS
|
|
||||||
|
|
||||||
SCALEFMT_NN ScaleFormat = 120
|
|
||||||
SCALEFMT_BILINEAR ScaleFormat = 121
|
|
||||||
SCALEFMT__DEFAULT = SCALEFMT_BILINEAR
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -37,123 +10,19 @@ var (
|
|||||||
ErrUnsupportedFiletype error = errors.New("Unsupported filetype")
|
ErrUnsupportedFiletype error = errors.New("Unsupported filetype")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Thumbnailer struct {
|
type Thumbnailer interface {
|
||||||
width int
|
RenderFile(absPath string) ([]byte, error)
|
||||||
height int
|
RenderFileAs(absPath, mimeType string) ([]byte, error)
|
||||||
ofmt OutputFormat
|
|
||||||
afmt AspectFormat
|
|
||||||
sfmt ScaleFormat
|
|
||||||
|
|
||||||
thumbCache *lru.Cache // threadsafe, might be nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewThumbnailer(Width, Height int, MaxCacheSize uint) *Thumbnailer {
|
|
||||||
return NewThumbnailerEx(Width, Height, MaxCacheSize, OUTPUT__DEFAULT, ASPECT__DEFAULT, SCALEFMT__DEFAULT)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewThumbnailerEx(Width, Height int, MaxCacheSize uint, of OutputFormat, af AspectFormat, sf ScaleFormat) *Thumbnailer {
|
|
||||||
|
|
||||||
ret := &Thumbnailer{
|
|
||||||
width: Width,
|
|
||||||
height: Height,
|
|
||||||
ofmt: of,
|
|
||||||
afmt: af,
|
|
||||||
sfmt: sf,
|
|
||||||
thumbCache: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
if MaxCacheSize > 0 {
|
|
||||||
thumbCache, err := lru.New(int(MaxCacheSize))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
ret.thumbCache = thumbCache
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Thumbnailer) Width() int {
|
|
||||||
return this.width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Thumbnailer) Height() int {
|
|
||||||
return this.height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
|
|
||||||
|
|
||||||
if this.thumbCache != nil {
|
|
||||||
thumb, ok := this.thumbCache.Get(absPath)
|
|
||||||
if ok {
|
|
||||||
return thumb.([]byte), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to cache
|
|
||||||
thumb, err := this.RenderFile_NoCache(absPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.thumbCache != nil {
|
|
||||||
this.thumbCache.Add(absPath, thumb)
|
|
||||||
}
|
|
||||||
|
|
||||||
return thumb, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FiletypeSupported(ext string) bool {
|
func FiletypeSupported(ext string) bool {
|
||||||
switch strings.ToLower(ext) {
|
switch strings.ToLower(ext) {
|
||||||
case ".jpg", ".jpeg", ".png", ".gif", ".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb":
|
case
|
||||||
|
".jpg", ".jpeg", ".png", ".gif",
|
||||||
|
".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb",
|
||||||
|
".bmp", ".webp":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
|
||||||
return this.RenderFile_NoCache_MimeType(absPath, mime.TypeByExtension(filepath.Ext(absPath)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderFile_NoCache_MimeType(absPath, mimeType string) ([]byte, error) {
|
|
||||||
|
|
||||||
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer fh.Close()
|
|
||||||
|
|
||||||
if mimeType == `image/jpeg` {
|
|
||||||
src, err := jpeg.Decode(fh)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.RenderScaledImage(src)
|
|
||||||
|
|
||||||
} else if mimeType == `image/png` {
|
|
||||||
src, err := png.Decode(fh)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.RenderScaledImage(src)
|
|
||||||
|
|
||||||
} else if mimeType == `image/gif` {
|
|
||||||
src, err := gif.Decode(fh)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.RenderScaledImage(src)
|
|
||||||
|
|
||||||
} else if strings.HasPrefix(mimeType, `video/`) {
|
|
||||||
return this.RenderScaledFfmpeg(absPath)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, ErrUnsupportedFiletype
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
A thumbnailing library for Go.
|
|
||||||
|
|
||||||
Written in Go
|
|
||||||
|
|
||||||
Earlier versions of this project were vendored into `webdir`.
|
|
||||||
|
|
||||||
- Supports jpeg / png / gif files (internally) and video files (requires `ffmpeg` in `$PATH`)
|
|
||||||
- LRU cache of recent thumbnails for performance
|
|
||||||
- Scaling algorithms: Nearest-neighbour or bilinear
|
|
||||||
- Aspect ratio preserving (fit outside with image crop; fit inside with transparent background; fit inside with dimension reduction)
|
|
||||||
- Output formats: JPG (internal) or transparent PNG (via `go-imagequant`)
|
|
||||||
|
|
||||||
A standalone binary `mkthumb` is provided as a sample utility.
|
|
||||||
|
|
||||||
=CHANGELOG=
|
|
||||||
|
|
||||||
2018-05-04 0.2.1
|
|
||||||
- Add `disableimagecrush` build tag option to exclude cgo library dependency
|
|
||||||
|
|
||||||
2017-11-18 0.2.0
|
|
||||||
- Initial standalone release
|
|
||||||
- Feature: Decode input with specified mime type
|
|
||||||
- Feature: Allow passing zero as thumbnail cache size
|
|
||||||
|
|
||||||
2017-01-03 0.1.0
|
|
||||||
- Version of `thumbnail` vendored with `webdir` 1.0 (previously tagged as `release-1.0`)
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package thumbnail
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Blend(a, b color.Color) color.Color {
|
|
||||||
switch a.(type) {
|
|
||||||
case color.RGBA:
|
|
||||||
return BlendRGBA(a.(color.RGBA), b.(color.RGBA)) // FIXME there's syntax for this
|
|
||||||
|
|
||||||
case color.YCbCr:
|
|
||||||
return BlendYCbCr(a.(color.YCbCr), b.(color.YCbCr))
|
|
||||||
|
|
||||||
default:
|
|
||||||
return a // ??? unknown color format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BlendYCbCr(a, b color.YCbCr) color.YCbCr {
|
|
||||||
return color.YCbCr{
|
|
||||||
Y: uint8((int(a.Y) + int(b.Y)) / 2),
|
|
||||||
Cb: uint8((int(a.Cb) + int(b.Cb)) / 2),
|
|
||||||
Cr: uint8((int(a.Cr) + int(b.Cr)) / 2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BlendRGBA(a, b color.RGBA) color.RGBA {
|
|
||||||
return color.RGBA{
|
|
||||||
R: uint8((int(a.R) + int(b.R)) / 2),
|
|
||||||
G: uint8((int(a.G) + int(b.G)) / 2),
|
|
||||||
B: uint8((int(a.B) + int(b.B)) / 2),
|
|
||||||
A: uint8((int(a.A) + int(b.A)) / 2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,8 +20,8 @@ func main() {
|
|||||||
|
|
||||||
fname := flag.Arg(0)
|
fname := flag.Arg(0)
|
||||||
|
|
||||||
th := thumbnail.NewThumbnailer(*width, *height, 1)
|
th := thumbnail.NewThumbnailer(*width, *height)
|
||||||
data, err := th.RenderFile_NoCache(fname)
|
data, err := th.RenderFile(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
37
config.go
Normal file
37
config.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
type OutputFormat uint8
|
||||||
|
type AspectFormat uint8
|
||||||
|
type ScaleFormat uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Png OutputFormat = 1
|
||||||
|
PngCrush OutputFormat = 2
|
||||||
|
Jpeg OutputFormat = 3
|
||||||
|
Bmp OutputFormat = 4
|
||||||
|
|
||||||
|
FitOutside AspectFormat = 80 // Pad out with black bars to dimensions
|
||||||
|
FitInside AspectFormat = 82 // Crop to dimensions
|
||||||
|
Stretch AspectFormat = 83 // Doesn't preserve aspect ratio
|
||||||
|
|
||||||
|
NearestNeighbour ScaleFormat = 120
|
||||||
|
BilinearFast ScaleFormat = 121
|
||||||
|
BilinearAccurate ScaleFormat = 122
|
||||||
|
Bicubic ScaleFormat = 123
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
Output OutputFormat
|
||||||
|
Aspect AspectFormat
|
||||||
|
Scale ScaleFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
Width: 128,
|
||||||
|
Height: 128,
|
||||||
|
Output: Jpeg,
|
||||||
|
Aspect: FitOutside,
|
||||||
|
Scale: Bicubic,
|
||||||
|
}
|
||||||
43
decodeImage.go
Normal file
43
decodeImage.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/webp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type imageDecoder func(io.Reader) (image.Image, error)
|
||||||
|
|
||||||
|
var imageDecoders map[string]imageDecoder = map[string]imageDecoder{
|
||||||
|
`image/jpeg`: jpeg.Decode,
|
||||||
|
`image/png`: png.Decode,
|
||||||
|
`image/gif`: gif.Decode,
|
||||||
|
`image/webp`: webp.Decode,
|
||||||
|
`image/bmp`: bmp.Decode,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) imageSnapshot(absPath, mimeType string) (image.Image, error) {
|
||||||
|
|
||||||
|
// Load file from path
|
||||||
|
|
||||||
|
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
// Decode image
|
||||||
|
|
||||||
|
fn, ok := imageDecoders[mimeType]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrUnsupportedFiletype
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(fh)
|
||||||
|
}
|
||||||
52
decodeVideo.go
Normal file
52
decodeVideo.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) videoSnapshot(absPath string) (image.Image, error) {
|
||||||
|
|
||||||
|
cmd := exec.Command(
|
||||||
|
"ffmpeg",
|
||||||
|
"-loglevel", "0",
|
||||||
|
"-timelimit", "10", // seconds
|
||||||
|
"-an",
|
||||||
|
"-i", absPath,
|
||||||
|
"-vf", `thumbnail`,
|
||||||
|
"-frames:v", "1",
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-c:v", "png", // always PNG output - we will resample/rescale it ourselves
|
||||||
|
"-",
|
||||||
|
)
|
||||||
|
|
||||||
|
// -ss 00:00:30
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.Buffer{}
|
||||||
|
_, err = io.Copy(&out, stdout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to decode as PNG image
|
||||||
|
|
||||||
|
return png.Decode(bytes.NewReader(out.Bytes()))
|
||||||
|
}
|
||||||
3
doc.go
3
doc.go
@@ -1,3 +0,0 @@
|
|||||||
// Package thumbnail contains functions for taking the thumbnail of image and
|
|
||||||
// video files, and caching the result for performance.
|
|
||||||
package thumbnail
|
|
||||||
7
go.mod
Normal file
7
go.mod
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module code.ivysaur.me/thumbnail
|
||||||
|
|
||||||
|
require (
|
||||||
|
code.ivysaur.me/imagequant v2.12.2-go1.2+incompatible
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0
|
||||||
|
golang.org/x/image v0.0.0-20180601115456-af66defab954
|
||||||
|
)
|
||||||
12
go.sum
Normal file
12
go.sum
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
code.ivysaur.me/imagequant v0.0.0-20180609052806-6a468707fb1b h1:bVCFA91vhlKDRLtb/32eu02fPtHKxK4TpCkX6oJxXV8=
|
||||||
|
code.ivysaur.me/imagequant v0.0.0-20180609052806-6a468707fb1b/go.mod h1:1Pi+M0oJFDYLtGuCkPGPpb4OGCYudvp/SG6jdVcO+WU=
|
||||||
|
code.ivysaur.me/imagequant v2.9.0-go1.1+incompatible h1:Sh8PH5ED6J6nMj2YbsSGmh1+Q/6CVTObITsXmHXsyC8=
|
||||||
|
code.ivysaur.me/imagequant v2.9.0-go1.1+incompatible/go.mod h1:1Pi+M0oJFDYLtGuCkPGPpb4OGCYudvp/SG6jdVcO+WU=
|
||||||
|
code.ivysaur.me/imagequant v2.12.2-go1.2+incompatible h1:lae+MZwMGir1Llekj8SUm/GS8Vu223v0rYbVQNv85Q4=
|
||||||
|
code.ivysaur.me/imagequant v2.12.2-go1.2+incompatible/go.mod h1:1Pi+M0oJFDYLtGuCkPGPpb4OGCYudvp/SG6jdVcO+WU=
|
||||||
|
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
|
||||||
|
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
golang.org/x/image v0.0.0-20180601115456-af66defab954 h1:n7UB+yxe5jyWxOA3BTAfwi23lhfKEIddaB/so7YOYe0=
|
||||||
|
golang.org/x/image v0.0.0-20180601115456-af66defab954/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
77
image.go
77
image.go
@@ -1,77 +0,0 @@
|
|||||||
package thumbnail
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"image"
|
|
||||||
"image/jpeg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderScaledImage(src image.Image) ([]byte, error) {
|
|
||||||
srcW := src.Bounds().Max.X
|
|
||||||
srcH := src.Bounds().Max.Y
|
|
||||||
destW := 0
|
|
||||||
destH := 0
|
|
||||||
|
|
||||||
if srcW > srcH {
|
|
||||||
destW = this.width
|
|
||||||
destH = this.height * srcH / srcW
|
|
||||||
} else {
|
|
||||||
destW = this.width * srcW / srcH
|
|
||||||
destH = this.height
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetX := (this.width - destW) / 2
|
|
||||||
offsetY := (this.height - destH) / 2
|
|
||||||
|
|
||||||
scaleW := float64(srcW) / float64(destW)
|
|
||||||
scaleH := float64(srcH) / float64(destH)
|
|
||||||
|
|
||||||
dest := image.NewRGBA(image.Rectangle{Max: image.Point{X: this.width, Y: this.height}})
|
|
||||||
|
|
||||||
switch this.sfmt {
|
|
||||||
|
|
||||||
case SCALEFMT_BILINEAR:
|
|
||||||
|
|
||||||
for y := 0; y < destH; y += 1 {
|
|
||||||
for x := 0; x < destW; x += 1 {
|
|
||||||
c00 := src.At(int(float64(x)*scaleW), int(float64(y)*scaleH))
|
|
||||||
c01 := src.At(int((float64(x)+0.5)*scaleW), int(float64(y)*scaleH))
|
|
||||||
c10 := src.At(int(float64(x)*scaleW), int((float64(y)+0.5)*scaleH))
|
|
||||||
c11 := src.At(int((float64(x)+0.5)*scaleW), int((float64(y)+0.5)*scaleH))
|
|
||||||
cBlend := Blend(Blend(c00, c01), Blend(c10, c11))
|
|
||||||
dest.Set(x+offsetX, y+offsetY, cBlend)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case SCALEFMT_NN:
|
|
||||||
|
|
||||||
for y := 0; y < destH; y += 1 {
|
|
||||||
for x := 0; x < destW; x += 1 {
|
|
||||||
mapx := int(float64(x) * scaleW)
|
|
||||||
mapy := int(float64(y) * scaleH)
|
|
||||||
dest.Set(x+offsetX, y+offsetY, src.At(mapx, mapy))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch this.ofmt {
|
|
||||||
case OUTPUT_PNG_CRUSH:
|
|
||||||
return crushFast(dest)
|
|
||||||
|
|
||||||
case OUTPUT_JPG:
|
|
||||||
buff := bytes.Buffer{}
|
|
||||||
err := jpeg.Encode(&buff, dest, &jpeg.Options{Quality: jpeg.DefaultQuality})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buff.Bytes(), nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidOption
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build !disableimagecrush
|
//+build withimagecrush
|
||||||
|
|
||||||
package thumbnail
|
package thumbnail
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build disableimagecrush
|
// +build !withimagecrush
|
||||||
|
|
||||||
package thumbnail
|
package thumbnail
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
hg archive out.zip
|
XZ_OPTS=-9 tar caf out.tar.xz --owner=0 --group=0 *.go Gopkg* cmd/mkthumb/*.go _dist/README.txt
|
||||||
|
|||||||
122
scaler.go
Normal file
122
scaler.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/draw"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) scaleImage(src image.Image) (image.Image, error) {
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
var srcCopyPosition, destCopyPosition image.Rectangle
|
||||||
|
|
||||||
|
switch this.cfg.Aspect {
|
||||||
|
case FitInside:
|
||||||
|
|
||||||
|
var destW, destH int
|
||||||
|
|
||||||
|
if srcW > srcH {
|
||||||
|
destW = this.cfg.Width
|
||||||
|
destH = this.cfg.Height * srcH / srcW
|
||||||
|
} else {
|
||||||
|
destW = this.cfg.Width * srcW / srcH
|
||||||
|
destH = this.cfg.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
offsetX := (this.cfg.Width - destW) / 2
|
||||||
|
offsetY := (this.cfg.Height - destH) / 2
|
||||||
|
|
||||||
|
srcCopyPosition = src.Bounds()
|
||||||
|
destCopyPosition = image.Rect(offsetX, offsetY, destW+offsetX, destH+offsetY)
|
||||||
|
|
||||||
|
case FitOutside:
|
||||||
|
|
||||||
|
var srcSmallestDim, offsetX, offsetY int
|
||||||
|
if srcW > srcH {
|
||||||
|
// Landscape
|
||||||
|
srcSmallestDim = srcH
|
||||||
|
offsetY = 0
|
||||||
|
offsetX = (srcW - srcH) / 2
|
||||||
|
} else {
|
||||||
|
// Portrait (or square)
|
||||||
|
srcSmallestDim = srcW
|
||||||
|
offsetY = (srcH - srcW) / 2
|
||||||
|
offsetX = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
srcCopyPosition = image.Rect(offsetX, offsetY, srcSmallestDim+offsetX, srcSmallestDim+offsetY)
|
||||||
|
destCopyPosition = image.Rect(0, 0, this.cfg.Width, this.cfg.Height)
|
||||||
|
|
||||||
|
case Stretch:
|
||||||
|
srcCopyPosition = src.Bounds()
|
||||||
|
destCopyPosition = image.Rect(0, 0, this.cfg.Width, this.cfg.Height)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidOption
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
dest := image.NewRGBA(image.Rect(0, 0, this.cfg.Width, this.cfg.Height))
|
||||||
|
|
||||||
|
// For a transparent destination, Op.Src is faster than Op.Over
|
||||||
|
|
||||||
|
switch this.cfg.Scale {
|
||||||
|
case NearestNeighbour:
|
||||||
|
draw.NearestNeighbor.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
|
|
||||||
|
case BilinearFast:
|
||||||
|
draw.ApproxBiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
|
|
||||||
|
case BilinearAccurate:
|
||||||
|
draw.BiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
|
|
||||||
|
case Bicubic:
|
||||||
|
draw.CatmullRom.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DirectThumbnailer) encode(dest image.Image) ([]byte, error) {
|
||||||
|
|
||||||
|
switch this.cfg.Output {
|
||||||
|
case Png:
|
||||||
|
buff := bytes.Buffer{}
|
||||||
|
err := png.Encode(&buff, dest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buff.Bytes(), nil
|
||||||
|
|
||||||
|
case PngCrush:
|
||||||
|
return crushFast(dest)
|
||||||
|
|
||||||
|
case Jpeg:
|
||||||
|
buff := bytes.Buffer{}
|
||||||
|
err := jpeg.Encode(&buff, dest, &jpeg.Options{Quality: jpeg.DefaultQuality})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff.Bytes(), nil
|
||||||
|
|
||||||
|
case Bmp:
|
||||||
|
buff := bytes.Buffer{}
|
||||||
|
err := bmp.Encode(&buff, dest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buff.Bytes(), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidOption
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
vendor/code.ivysaur.me/imagequant/.gitignore
generated
vendored
Normal file
2
vendor/code.ivysaur.me/imagequant/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cmd/gopngquant/gopngquant
|
||||||
|
cmd/gopngquant/gopngquant.exe
|
||||||
114
vendor/code.ivysaur.me/imagequant/Attributes.go
generated
vendored
Normal file
114
vendor/code.ivysaur.me/imagequant/Attributes.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, The go-imagequant author(s)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||||
|
IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "libimagequant.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Attributes struct {
|
||||||
|
p *C.struct_liq_attr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callers MUST call Release() on the returned object to free memory.
|
||||||
|
func NewAttributes() (*Attributes, error) {
|
||||||
|
pAttr := C.liq_attr_create()
|
||||||
|
if pAttr == nil { // nullptr
|
||||||
|
return nil, errors.New("Unsupported platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Attributes{p: pAttr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
COLORS_MIN = 2
|
||||||
|
COLORS_MAX = 256
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *Attributes) SetMaxColors(colors int) error {
|
||||||
|
return translateError(C.liq_set_max_colors(this.p, C.int(colors)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetMaxColors() int {
|
||||||
|
return int(C.liq_get_max_colors(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
QUALITY_MIN = 0
|
||||||
|
QUALITY_MAX = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *Attributes) SetQuality(minimum, maximum int) error {
|
||||||
|
return translateError(C.liq_set_quality(this.p, C.int(minimum), C.int(maximum)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetMinQuality() int {
|
||||||
|
return int(C.liq_get_min_quality(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetMaxQuality() int {
|
||||||
|
return int(C.liq_get_max_quality(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SPEED_SLOWEST = 1
|
||||||
|
SPEED_DEFAULT = 3
|
||||||
|
SPEED_FASTEST = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *Attributes) SetSpeed(speed int) error {
|
||||||
|
return translateError(C.liq_set_speed(this.p, C.int(speed)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetSpeed() int {
|
||||||
|
return int(C.liq_get_speed(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) SetMinOpacity(min int) error {
|
||||||
|
return translateError(C.liq_set_min_opacity(this.p, C.int(min)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetMinOpacity() int {
|
||||||
|
return int(C.liq_get_min_opacity(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) SetMinPosterization(bits int) error {
|
||||||
|
return translateError(C.liq_set_min_posterization(this.p, C.int(bits)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) GetMinPosterization() int {
|
||||||
|
return int(C.liq_get_min_posterization(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) SetLastIndexTransparent(is_last int) {
|
||||||
|
C.liq_set_last_index_transparent(this.p, C.int(is_last))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Attributes) CreateHistogram() *Histogram {
|
||||||
|
ptr := C.liq_histogram_create(this.p)
|
||||||
|
return &Histogram{p: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory. Callers must not use this object after Release has been called.
|
||||||
|
func (this *Attributes) Release() {
|
||||||
|
C.liq_attr_destroy(this.p)
|
||||||
|
}
|
||||||
641
vendor/code.ivysaur.me/imagequant/COPYRIGHT
generated
vendored
Normal file
641
vendor/code.ivysaur.me/imagequant/COPYRIGHT
generated
vendored
Normal file
@@ -0,0 +1,641 @@
|
|||||||
|
|
||||||
|
libimagequant is derived from code by Jef Poskanzer and Greg Roelofs
|
||||||
|
licensed under pngquant's original license (at the end of this file),
|
||||||
|
and contains extensive changes and additions by Kornel Lesiński
|
||||||
|
licensed under GPL v3.
|
||||||
|
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
libimagequant © 2009-2016 by Kornel Lesiński.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
© 1989, 1991 by Jef Poskanzer.
|
||||||
|
© 1997, 2000, 2002 by Greg Roelofs.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose and without fee is hereby granted, provided
|
||||||
|
that the above copyright notice appear in all copies and that both that
|
||||||
|
copyright notice and this permission notice appear in supporting
|
||||||
|
documentation. This software is provided "as is" without express or
|
||||||
|
implied warranty.
|
||||||
45
vendor/code.ivysaur.me/imagequant/Histogram.go
generated
vendored
Normal file
45
vendor/code.ivysaur.me/imagequant/Histogram.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, The go-imagequant author(s)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||||
|
IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "libimagequant.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Histogram struct {
|
||||||
|
p *C.struct_liq_histogram
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Histogram) AddImage(attr *Attributes, img *Image) error {
|
||||||
|
return translateError(C.liq_histogram_add_image(this.p, attr.p, img.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Histogram) Quantize(attr *Attributes) (*Result, error) {
|
||||||
|
res := Result{}
|
||||||
|
liqerr := C.liq_histogram_quantize(this.p, attr.p, &res.p)
|
||||||
|
if liqerr != C.LIQ_OK {
|
||||||
|
return nil, translateError(liqerr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory. Callers must not use this object after Release has been called.
|
||||||
|
func (this *Histogram) Release() {
|
||||||
|
C.liq_histogram_destroy(this.p)
|
||||||
|
}
|
||||||
66
vendor/code.ivysaur.me/imagequant/Image.go
generated
vendored
Normal file
66
vendor/code.ivysaur.me/imagequant/Image.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, The go-imagequant author(s)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||||
|
IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "libimagequant.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
p *C.struct_liq_image
|
||||||
|
w, h int
|
||||||
|
released bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callers MUST call Release() on the returned object to free memory.
|
||||||
|
func NewImage(attr *Attributes, rgba32data string, width, height int, gamma float64) (*Image, error) {
|
||||||
|
pImg := C.liq_image_create_rgba(attr.p, unsafe.Pointer(C.CString(rgba32data)), C.int(width), C.int(height), C.double(gamma))
|
||||||
|
if pImg == nil {
|
||||||
|
return nil, errors.New("Failed to create image (invalid argument)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Image{
|
||||||
|
p: pImg,
|
||||||
|
w: width,
|
||||||
|
h: height,
|
||||||
|
released: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory. Callers must not use this object after Release has been called.
|
||||||
|
func (this *Image) Release() {
|
||||||
|
C.liq_image_destroy(this.p)
|
||||||
|
this.released = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Image) Quantize(attr *Attributes) (*Result, error) {
|
||||||
|
res := Result{
|
||||||
|
im: this,
|
||||||
|
}
|
||||||
|
liqerr := C.liq_image_quantize(this.p, attr.p, &res.p)
|
||||||
|
if liqerr != C.LIQ_OK {
|
||||||
|
return nil, translateError(liqerr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
65
vendor/code.ivysaur.me/imagequant/README.md
generated
vendored
Normal file
65
vendor/code.ivysaur.me/imagequant/README.md
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# imagequant
|
||||||
|
|
||||||
|
Go bindings for libimagequant
|
||||||
|
|
||||||
|
`libimagequant` is a library for lossy recompression of PNG images to reduce their filesize. It is used by the `pngquant` tool. This `go-imagequant` project is a set of bindings for libimagequant to enable its use from the Go programming language.
|
||||||
|
|
||||||
|
This binding was written by hand. The result is somewhat more idiomatic than an automated conversion, but some `defer foo.Release()` calls are required for memory management.
|
||||||
|
|
||||||
|
Written in Golang
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Usage example is provided by a sample utility `cmd/gopngquant` which mimics some functionality of the upstream `pngquant`.
|
||||||
|
|
||||||
|
The sample utility has the following options:
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage of gopngquant:
|
||||||
|
-In string
|
||||||
|
Input filename
|
||||||
|
-Out string
|
||||||
|
Output filename
|
||||||
|
-Speed int
|
||||||
|
Speed (1 slowest, 10 fastest) (default 3)
|
||||||
|
-Version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
This package can be installed via go get: `go get code.ivysaur.me/imagequant`
|
||||||
|
[go-get]code.ivysaur.me/imagequant git https://git.ivysaur.me/code.ivysaur.me/imagequant.git[/go-get]
|
||||||
|
|
||||||
|
The expected package path is `code.ivysaur.me/imagequant`. Build via `go build`.
|
||||||
|
|
||||||
|
This is a CGO package and requires a C compiler installed. However, if you use `go install` then future invocations of `go build` do not require the C compiler to be present.
|
||||||
|
|
||||||
|
The `imagequant.go` file also declares a number of `CFLAGS` for GCC that allow the included libimagequant (2.8 git-a425e83) to build in an optimal way without using the upstream configure/make scripts.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
I am releasing this binding under the ISC license, however, `libimagequant` itself is released under GPLv3-or-later and/or commercial licenses. You must comply with the terms of such a license when using this binding in a Go project.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
2018-12-31 v2.12.2-go1.2
|
||||||
|
- go-imagequant: Update bundled libimagequant from 2.9.0 to 2.12.2
|
||||||
|
- build: Switch to Go Modules
|
||||||
|
- build: Update bundled CFLAGS for new CGo whitelist (reduces performance)
|
||||||
|
- build: Remove nonportable Cygwin makefile
|
||||||
|
|
||||||
|
2017-03-03 v2.9.0-go1.1
|
||||||
|
- *Previously tagged as 2.9go1.1*
|
||||||
|
- go-imagequant: Update bundled libimagequant from 2.8.0 to 2.9.0
|
||||||
|
- go-imagequant: Separate `CGO_LDFLAGS` for Linux and Windows targets
|
||||||
|
- gopngquant: Fix an issue with non-square images
|
||||||
|
|
||||||
|
2016-11-24 v2.8.0-go1.0
|
||||||
|
- *Previously tagged as 2.8go1.0*
|
||||||
|
- Initial public release
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- Pngquant homepage https://pngquant.org/
|
||||||
|
- Pngquant source code https://github.com/pornel/pngquant
|
||||||
|
- Libimagequant source code https://github.com/ImageOptim/libimagequant
|
||||||
110
vendor/code.ivysaur.me/imagequant/Result.go
generated
vendored
Normal file
110
vendor/code.ivysaur.me/imagequant/Result.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, The go-imagequant author(s)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||||
|
IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "libimagequant.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Callers must not use this object once Release has been called on the parent
|
||||||
|
// Image struct.
|
||||||
|
type Result struct {
|
||||||
|
p *C.struct_liq_result
|
||||||
|
im *Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) SetDitheringLevel(dither_level float32) error {
|
||||||
|
return translateError(C.liq_set_dithering_level(this.p, C.float(dither_level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetQuantizationError() float64 {
|
||||||
|
return float64(C.liq_get_quantization_error(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetRemappingError() float64 {
|
||||||
|
return float64(C.liq_get_remapping_error(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetQuantizationQuality() float64 {
|
||||||
|
return float64(C.liq_get_quantization_quality(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetRemappingQuality() float64 {
|
||||||
|
return float64(C.liq_get_remapping_quality(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) SetOutputGamma(gamma float64) error {
|
||||||
|
return translateError(C.liq_set_output_gamma(this.p, C.double(gamma)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetImageWidth() int {
|
||||||
|
// C.liq_image_get_width
|
||||||
|
return this.im.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetImageHeight() int {
|
||||||
|
// C.liq_image_get_height
|
||||||
|
return this.im.h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetOutputGamma() float64 {
|
||||||
|
return float64(C.liq_get_output_gamma(this.p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) WriteRemappedImage() ([]byte, error) {
|
||||||
|
if this.im.released {
|
||||||
|
return nil, ErrUseAfterFree
|
||||||
|
}
|
||||||
|
|
||||||
|
buff_size := this.im.w * this.im.h
|
||||||
|
buff := make([]byte, buff_size)
|
||||||
|
|
||||||
|
iqe := C.liq_write_remapped_image(this.p, this.im.p, unsafe.Pointer(&buff[0]), C.size_t(buff_size))
|
||||||
|
if iqe != C.LIQ_OK {
|
||||||
|
return nil, translateError(iqe)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Result) GetPalette() color.Palette {
|
||||||
|
ptr := C.liq_get_palette(this.p) // copy struct content
|
||||||
|
max := int(ptr.count)
|
||||||
|
|
||||||
|
ret := make([]color.Color, max)
|
||||||
|
for i := 0; i < max; i += 1 {
|
||||||
|
ret[i] = color.RGBA{
|
||||||
|
R: uint8(ptr.entries[i].r),
|
||||||
|
G: uint8(ptr.entries[i].g),
|
||||||
|
B: uint8(ptr.entries[i].b),
|
||||||
|
A: uint8(ptr.entries[i].a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory. Callers must not use this object after Release has been called.
|
||||||
|
func (this *Result) Release() {
|
||||||
|
C.liq_result_destroy(this.p)
|
||||||
|
}
|
||||||
132
vendor/code.ivysaur.me/imagequant/blur.c
generated
vendored
Normal file
132
vendor/code.ivysaur.me/imagequant/blur.c
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
© 2011-2015 by Kornel Lesiński.
|
||||||
|
|
||||||
|
This file is part of libimagequant.
|
||||||
|
|
||||||
|
libimagequant is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
libimagequant is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with libimagequant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "pam.h"
|
||||||
|
#include "blur.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Blurs image horizontally (width 2*size+1) and writes it transposed to dst (called twice gives 2d blur)
|
||||||
|
*/
|
||||||
|
static void transposing_1d_blur(unsigned char *restrict src, unsigned char *restrict dst, unsigned int width, unsigned int height, const unsigned int size)
|
||||||
|
{
|
||||||
|
assert(size > 0);
|
||||||
|
|
||||||
|
for(unsigned int j=0; j < height; j++) {
|
||||||
|
unsigned char *restrict row = src + j*width;
|
||||||
|
|
||||||
|
// accumulate sum for pixels outside line
|
||||||
|
unsigned int sum;
|
||||||
|
sum = row[0]*size;
|
||||||
|
for(unsigned int i=0; i < size; i++) {
|
||||||
|
sum += row[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// blur with left side outside line
|
||||||
|
for(unsigned int i=0; i < size; i++) {
|
||||||
|
sum -= row[0];
|
||||||
|
sum += row[i+size];
|
||||||
|
|
||||||
|
dst[i*height + j] = sum / (size*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int i=size; i < width-size; i++) {
|
||||||
|
sum -= row[i-size];
|
||||||
|
sum += row[i+size];
|
||||||
|
|
||||||
|
dst[i*height + j] = sum / (size*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// blur with right side outside line
|
||||||
|
for(unsigned int i=width-size; i < width; i++) {
|
||||||
|
sum -= row[i-size];
|
||||||
|
sum += row[width-1];
|
||||||
|
|
||||||
|
dst[i*height + j] = sum / (size*2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks maximum of neighboring pixels (blur + lighten)
|
||||||
|
*/
|
||||||
|
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
for(unsigned int j=0; j < height; j++) {
|
||||||
|
const unsigned char *row = src + j*width,
|
||||||
|
*prevrow = src + (j > 1 ? j-1 : 0)*width,
|
||||||
|
*nextrow = src + MIN(height-1,j+1)*width;
|
||||||
|
|
||||||
|
unsigned char prev,curr=row[0],next=row[0];
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < width-1; i++) {
|
||||||
|
prev=curr;
|
||||||
|
curr=next;
|
||||||
|
next=row[i+1];
|
||||||
|
|
||||||
|
unsigned char t1 = MAX(prev,next);
|
||||||
|
unsigned char t2 = MAX(nextrow[i],prevrow[i]);
|
||||||
|
*dst++ = MAX(curr,MAX(t1,t2));
|
||||||
|
}
|
||||||
|
unsigned char t1 = MAX(curr,next);
|
||||||
|
unsigned char t2 = MAX(nextrow[width-1],prevrow[width-1]);
|
||||||
|
*dst++ = MAX(t1,t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks minimum of neighboring pixels (blur + darken)
|
||||||
|
*/
|
||||||
|
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
for(unsigned int j=0; j < height; j++) {
|
||||||
|
const unsigned char *row = src + j*width,
|
||||||
|
*prevrow = src + (j > 1 ? j-1 : 0)*width,
|
||||||
|
*nextrow = src + MIN(height-1,j+1)*width;
|
||||||
|
|
||||||
|
unsigned char prev,curr=row[0],next=row[0];
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < width-1; i++) {
|
||||||
|
prev=curr;
|
||||||
|
curr=next;
|
||||||
|
next=row[i+1];
|
||||||
|
|
||||||
|
unsigned char t1 = MIN(prev,next);
|
||||||
|
unsigned char t2 = MIN(nextrow[i],prevrow[i]);
|
||||||
|
*dst++ = MIN(curr,MIN(t1,t2));
|
||||||
|
}
|
||||||
|
unsigned char t1 = MIN(curr,next);
|
||||||
|
unsigned char t2 = MIN(nextrow[width-1],prevrow[width-1]);
|
||||||
|
*dst++ = MIN(t1,t2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Filters src image and saves it to dst, overwriting tmp in the process.
|
||||||
|
Image must be width*height pixels high. Size controls radius of box blur.
|
||||||
|
*/
|
||||||
|
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size)
|
||||||
|
{
|
||||||
|
assert(size > 0);
|
||||||
|
if (width < 2*size+1 || height < 2*size+1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transposing_1d_blur(src, tmp, width, height, size);
|
||||||
|
transposing_1d_blur(tmp, dst, height, width, size);
|
||||||
|
}
|
||||||
4
vendor/code.ivysaur.me/imagequant/blur.h
generated
vendored
Normal file
4
vendor/code.ivysaur.me/imagequant/blur.h
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size);
|
||||||
|
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height);
|
||||||
|
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height);
|
||||||
9
vendor/code.ivysaur.me/imagequant/cflags_linux.go
generated
vendored
Normal file
9
vendor/code.ivysaur.me/imagequant/cflags_linux.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//+build !windows
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -O3 -fopenmp -fomit-frame-pointer -Wall -Wno-attributes -std=c99 -DNDEBUG -DUSE_SSE=1 -msse
|
||||||
|
#cgo LDFLAGS: -lm -fopenmp -ldl
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
9
vendor/code.ivysaur.me/imagequant/cflags_windows.go
generated
vendored
Normal file
9
vendor/code.ivysaur.me/imagequant/cflags_windows.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//+build windows
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -O3 -fno-math-errno -fopenmp -funroll-loops -fomit-frame-pointer -Wall -Wno-attributes -std=c99 -DNDEBUG -DUSE_SSE=1 -msse -fexcess-precision=fast
|
||||||
|
#cgo LDFLAGS: -fopenmp -static
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
1
vendor/code.ivysaur.me/imagequant/go.mod
generated
vendored
Normal file
1
vendor/code.ivysaur.me/imagequant/go.mod
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module code.ivysaur.me/imagequant
|
||||||
74
vendor/code.ivysaur.me/imagequant/imagequant.go
generated
vendored
Normal file
74
vendor/code.ivysaur.me/imagequant/imagequant.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, The go-imagequant author(s)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||||
|
IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package imagequant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "libimagequant.h"
|
||||||
|
|
||||||
|
const char* liqVersionString() {
|
||||||
|
return LIQ_VERSION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrQualityTooLow = errors.New("Quality too low")
|
||||||
|
ErrValueOutOfRange = errors.New("Value out of range")
|
||||||
|
ErrOutOfMemory = errors.New("Out of memory")
|
||||||
|
ErrAborted = errors.New("Aborted")
|
||||||
|
ErrBitmapNotAvailable = errors.New("Bitmap not available")
|
||||||
|
ErrBufferTooSmall = errors.New("Buffer too small")
|
||||||
|
ErrInvalidPointer = errors.New("Invalid pointer")
|
||||||
|
|
||||||
|
ErrUseAfterFree = errors.New("Use after free")
|
||||||
|
)
|
||||||
|
|
||||||
|
func translateError(iqe C.liq_error) error {
|
||||||
|
switch iqe {
|
||||||
|
case C.LIQ_OK:
|
||||||
|
return nil
|
||||||
|
case (C.LIQ_QUALITY_TOO_LOW):
|
||||||
|
return ErrQualityTooLow
|
||||||
|
case (C.LIQ_VALUE_OUT_OF_RANGE):
|
||||||
|
return ErrValueOutOfRange
|
||||||
|
case (C.LIQ_OUT_OF_MEMORY):
|
||||||
|
return ErrOutOfMemory
|
||||||
|
case (C.LIQ_ABORTED):
|
||||||
|
return ErrAborted
|
||||||
|
case (C.LIQ_BITMAP_NOT_AVAILABLE):
|
||||||
|
return ErrBitmapNotAvailable
|
||||||
|
case (C.LIQ_BUFFER_TOO_SMALL):
|
||||||
|
return ErrBufferTooSmall
|
||||||
|
case (C.LIQ_INVALID_POINTER):
|
||||||
|
return ErrInvalidPointer
|
||||||
|
default:
|
||||||
|
return errors.New("Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLibraryVersion() int {
|
||||||
|
return int(C.liq_version())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLibraryVersionString() string {
|
||||||
|
return C.GoString(C.liqVersionString())
|
||||||
|
}
|
||||||
93
vendor/code.ivysaur.me/imagequant/kmeans.c
generated
vendored
Normal file
93
vendor/code.ivysaur.me/imagequant/kmeans.c
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
** © 2011-2016 by Kornel Lesiński.
|
||||||
|
** See COPYRIGHT file for license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "pam.h"
|
||||||
|
#include "kmeans.h"
|
||||||
|
#include "nearest.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#else
|
||||||
|
#define omp_get_max_threads() 1
|
||||||
|
#define omp_get_thread_num() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* K-Means iteration: new palette color is computed from weighted average of colors that map to that palette entry.
|
||||||
|
*/
|
||||||
|
LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state average_color[])
|
||||||
|
{
|
||||||
|
memset(average_color, 0, sizeof(average_color[0])*(KMEANS_CACHE_LINE_GAP+map->colors)*max_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[])
|
||||||
|
{
|
||||||
|
match += thread * (KMEANS_CACHE_LINE_GAP+map->colors);
|
||||||
|
average_color[match].a += acolor.a * value;
|
||||||
|
average_color[match].r += acolor.r * value;
|
||||||
|
average_color[match].g += acolor.g * value;
|
||||||
|
average_color[match].b += acolor.b * value;
|
||||||
|
average_color[match].total += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state average_color[])
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i < map->colors; i++) {
|
||||||
|
double a=0, r=0, g=0, b=0, total=0;
|
||||||
|
|
||||||
|
// Aggregate results from all threads
|
||||||
|
for(unsigned int t=0; t < max_threads; t++) {
|
||||||
|
const unsigned int offset = (KMEANS_CACHE_LINE_GAP+map->colors) * t + i;
|
||||||
|
|
||||||
|
a += average_color[offset].a;
|
||||||
|
r += average_color[offset].r;
|
||||||
|
g += average_color[offset].g;
|
||||||
|
b += average_color[offset].b;
|
||||||
|
total += average_color[offset].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total && !map->palette[i].fixed) {
|
||||||
|
map->palette[i].acolor = (f_pixel){
|
||||||
|
.a = a / total,
|
||||||
|
.r = r / total,
|
||||||
|
.g = g / total,
|
||||||
|
.b = b / total,
|
||||||
|
};
|
||||||
|
map->palette[i].popularity = total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback)
|
||||||
|
{
|
||||||
|
const unsigned int max_threads = omp_get_max_threads();
|
||||||
|
LIQ_ARRAY(kmeans_state, average_color, (KMEANS_CACHE_LINE_GAP+map->colors) * max_threads);
|
||||||
|
kmeans_init(map, max_threads, average_color);
|
||||||
|
struct nearest_map *const n = nearest_init(map);
|
||||||
|
hist_item *const achv = hist->achv;
|
||||||
|
const int hist_size = hist->size;
|
||||||
|
|
||||||
|
double total_diff=0;
|
||||||
|
#pragma omp parallel for if (hist_size > 2000) \
|
||||||
|
schedule(static) default(none) shared(average_color,callback) reduction(+:total_diff)
|
||||||
|
for(int j=0; j < hist_size; j++) {
|
||||||
|
float diff;
|
||||||
|
unsigned int match = nearest_search(n, &achv[j].acolor, achv[j].tmp.likely_colormap_index, &diff);
|
||||||
|
achv[j].tmp.likely_colormap_index = match;
|
||||||
|
total_diff += diff * achv[j].perceptual_weight;
|
||||||
|
|
||||||
|
kmeans_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, omp_get_thread_num(), average_color);
|
||||||
|
|
||||||
|
if (callback) callback(&achv[j], diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
nearest_free(n);
|
||||||
|
kmeans_finalize(map, max_threads, average_color);
|
||||||
|
|
||||||
|
return total_diff / hist->total_perceptual_weight;
|
||||||
|
}
|
||||||
19
vendor/code.ivysaur.me/imagequant/kmeans.h
generated
vendored
Normal file
19
vendor/code.ivysaur.me/imagequant/kmeans.h
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
#ifndef KMEANS_H
|
||||||
|
#define KMEANS_H
|
||||||
|
|
||||||
|
// Spread memory touched by different threads at least 64B apart which I assume is the cache line size. This should avoid memory write contention.
|
||||||
|
#define KMEANS_CACHE_LINE_GAP ((64+sizeof(kmeans_state)-1)/sizeof(kmeans_state))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double a, r, g, b, total;
|
||||||
|
} kmeans_state;
|
||||||
|
|
||||||
|
typedef void (*kmeans_callback)(hist_item *item, float diff);
|
||||||
|
|
||||||
|
LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state state[]);
|
||||||
|
LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]);
|
||||||
|
LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state state[]);
|
||||||
|
LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback);
|
||||||
|
|
||||||
|
#endif
|
||||||
2159
vendor/code.ivysaur.me/imagequant/libimagequant.c
generated
vendored
Normal file
2159
vendor/code.ivysaur.me/imagequant/libimagequant.c
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
151
vendor/code.ivysaur.me/imagequant/libimagequant.h
generated
vendored
Normal file
151
vendor/code.ivysaur.me/imagequant/libimagequant.h
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* https://pngquant.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBIMAGEQUANT_H
|
||||||
|
#define LIBIMAGEQUANT_H
|
||||||
|
|
||||||
|
#ifdef IMAGEQUANT_EXPORTS
|
||||||
|
#define LIQ_EXPORT __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LIQ_EXPORT
|
||||||
|
#define LIQ_EXPORT extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIQ_VERSION 21200
|
||||||
|
#define LIQ_VERSION_STRING "2.12.2"
|
||||||
|
|
||||||
|
#ifndef LIQ_PRIVATE
|
||||||
|
#if defined(__GNUC__) || defined (__llvm__)
|
||||||
|
#define LIQ_PRIVATE __attribute__((visibility("hidden")))
|
||||||
|
#define LIQ_NONNULL __attribute__((nonnull))
|
||||||
|
#define LIQ_USERESULT __attribute__((warn_unused_result))
|
||||||
|
#else
|
||||||
|
#define LIQ_PRIVATE
|
||||||
|
#define LIQ_NONNULL
|
||||||
|
#define LIQ_USERESULT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct liq_attr liq_attr;
|
||||||
|
typedef struct liq_image liq_image;
|
||||||
|
typedef struct liq_result liq_result;
|
||||||
|
typedef struct liq_histogram liq_histogram;
|
||||||
|
|
||||||
|
typedef struct liq_color {
|
||||||
|
unsigned char r, g, b, a;
|
||||||
|
} liq_color;
|
||||||
|
|
||||||
|
typedef struct liq_palette {
|
||||||
|
unsigned int count;
|
||||||
|
liq_color entries[256];
|
||||||
|
} liq_palette;
|
||||||
|
|
||||||
|
typedef enum liq_error {
|
||||||
|
LIQ_OK = 0,
|
||||||
|
LIQ_QUALITY_TOO_LOW = 99,
|
||||||
|
LIQ_VALUE_OUT_OF_RANGE = 100,
|
||||||
|
LIQ_OUT_OF_MEMORY,
|
||||||
|
LIQ_ABORTED,
|
||||||
|
LIQ_BITMAP_NOT_AVAILABLE,
|
||||||
|
LIQ_BUFFER_TOO_SMALL,
|
||||||
|
LIQ_INVALID_POINTER,
|
||||||
|
LIQ_UNSUPPORTED,
|
||||||
|
} liq_error;
|
||||||
|
|
||||||
|
enum liq_ownership {
|
||||||
|
LIQ_OWN_ROWS=4,
|
||||||
|
LIQ_OWN_PIXELS=8,
|
||||||
|
LIQ_COPY_PIXELS=16,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct liq_histogram_entry {
|
||||||
|
liq_color color;
|
||||||
|
unsigned int count;
|
||||||
|
} liq_histogram_entry;
|
||||||
|
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create(void);
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create_with_allocator(void* (*malloc)(size_t), void (*free)(void*));
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_copy(const liq_attr *orig) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT void liq_attr_destroy(liq_attr *attr) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_histogram* liq_histogram_create(const liq_attr* attr);
|
||||||
|
LIQ_EXPORT liq_error liq_histogram_add_image(liq_histogram *hist, const liq_attr *attr, liq_image* image) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_histogram_add_colors(liq_histogram *hist, const liq_attr *attr, const liq_histogram_entry entries[], int num_entries, double gamma) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_histogram_add_fixed_color(liq_histogram *hist, liq_color color, double gamma) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT void liq_histogram_destroy(liq_histogram *hist) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT liq_error liq_set_max_colors(liq_attr* attr, int colors) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_max_colors(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_set_speed(liq_attr* attr, int speed) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_speed(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_set_min_opacity(liq_attr* attr, int min) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_min_opacity(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_set_min_posterization(liq_attr* attr, int bits) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_min_posterization(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_set_quality(liq_attr* attr, int minimum, int maximum) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_min_quality(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_get_max_quality(const liq_attr* attr) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT void liq_set_last_index_transparent(liq_attr* attr, int is_last) LIQ_NONNULL;
|
||||||
|
|
||||||
|
typedef void liq_log_callback_function(const liq_attr*, const char *message, void* user_info);
|
||||||
|
typedef void liq_log_flush_callback_function(const liq_attr*, void* user_info);
|
||||||
|
LIQ_EXPORT void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void* user_info);
|
||||||
|
LIQ_EXPORT void liq_set_log_flush_callback(liq_attr*, liq_log_flush_callback_function*, void* user_info);
|
||||||
|
|
||||||
|
typedef int liq_progress_callback_function(float progress_percent, void* user_info);
|
||||||
|
LIQ_EXPORT void liq_attr_set_progress_callback(liq_attr*, liq_progress_callback_function*, void* user_info);
|
||||||
|
LIQ_EXPORT void liq_result_set_progress_callback(liq_result*, liq_progress_callback_function*, void* user_info);
|
||||||
|
|
||||||
|
// The rows and their data are not modified. The type of `rows` is non-const only due to a bug in C's typesystem design.
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba_rows(const liq_attr *attr, void *const rows[], int width, int height, double gamma) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba(const liq_attr *attr, const void *bitmap, int width, int height, double gamma) LIQ_NONNULL;
|
||||||
|
|
||||||
|
typedef void liq_image_get_rgba_row_callback(liq_color row_out[], int row, int width, void* user_info);
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_custom(const liq_attr *attr, liq_image_get_rgba_row_callback *row_callback, void* user_info, int width, int height, double gamma);
|
||||||
|
|
||||||
|
LIQ_EXPORT liq_error liq_image_set_memory_ownership(liq_image *image, int ownership_flags) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_image_set_background(liq_image *img, liq_image *background_image) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_image_set_importance_map(liq_image *img, unsigned char buffer[], size_t buffer_size, enum liq_ownership memory_handling) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_image_add_fixed_color(liq_image *img, liq_color color) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_image_get_width(const liq_image *img) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT int liq_image_get_height(const liq_image *img) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT void liq_image_destroy(liq_image *img) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_error liq_histogram_quantize(liq_histogram *const input_hist, liq_attr *const options, liq_result **result_output) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_error liq_image_quantize(liq_image *const input_image, liq_attr *const options, liq_result **result_output) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT liq_error liq_set_dithering_level(liq_result *res, float dither_level) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_set_output_gamma(liq_result* res, double gamma) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT LIQ_USERESULT double liq_get_output_gamma(const liq_result *result) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT LIQ_USERESULT const liq_palette *liq_get_palette(liq_result *result) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT liq_error liq_write_remapped_image(liq_result *result, liq_image *input_image, void *buffer, size_t buffer_size) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT liq_error liq_write_remapped_image_rows(liq_result *result, liq_image *input_image, unsigned char **row_pointers) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT double liq_get_quantization_error(const liq_result *result) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT int liq_get_quantization_quality(const liq_result *result) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT double liq_get_remapping_error(const liq_result *result) LIQ_NONNULL;
|
||||||
|
LIQ_EXPORT int liq_get_remapping_quality(const liq_result *result) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT void liq_result_destroy(liq_result *) LIQ_NONNULL;
|
||||||
|
|
||||||
|
LIQ_EXPORT int liq_version(void);
|
||||||
|
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
LIQ_EXPORT LIQ_USERESULT liq_result *liq_quantize_image(liq_attr *options, liq_image *input_image) LIQ_NONNULL;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
469
vendor/code.ivysaur.me/imagequant/mediancut.c
generated
vendored
Normal file
469
vendor/code.ivysaur.me/imagequant/mediancut.c
generated
vendored
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
/*
|
||||||
|
** © 2009-2018 by Kornel Lesiński.
|
||||||
|
** © 1989, 1991 by Jef Poskanzer.
|
||||||
|
** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider.
|
||||||
|
**
|
||||||
|
** See COPYRIGHT file for license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "pam.h"
|
||||||
|
#include "mediancut.h"
|
||||||
|
|
||||||
|
#define index_of_channel(ch) (offsetof(f_pixel,ch)/sizeof(float))
|
||||||
|
|
||||||
|
static f_pixel averagepixels(unsigned int clrs, const hist_item achv[]);
|
||||||
|
|
||||||
|
struct box {
|
||||||
|
f_pixel color;
|
||||||
|
f_pixel variance;
|
||||||
|
double sum, total_error, max_error;
|
||||||
|
unsigned int ind;
|
||||||
|
unsigned int colors;
|
||||||
|
};
|
||||||
|
|
||||||
|
ALWAYS_INLINE static double variance_diff(double val, const double good_enough);
|
||||||
|
inline static double variance_diff(double val, const double good_enough)
|
||||||
|
{
|
||||||
|
val *= val;
|
||||||
|
if (val < good_enough*good_enough) return val*0.25;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Weighted per-channel variance of the box. It's used to decide which channel to split by */
|
||||||
|
static f_pixel box_variance(const hist_item achv[], const struct box *box)
|
||||||
|
{
|
||||||
|
f_pixel mean = box->color;
|
||||||
|
double variancea=0, variancer=0, varianceg=0, varianceb=0;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < box->colors; ++i) {
|
||||||
|
const f_pixel px = achv[box->ind + i].acolor;
|
||||||
|
double weight = achv[box->ind + i].adjusted_weight;
|
||||||
|
variancea += variance_diff(mean.a - px.a, 2.0/256.0)*weight;
|
||||||
|
variancer += variance_diff(mean.r - px.r, 1.0/256.0)*weight;
|
||||||
|
varianceg += variance_diff(mean.g - px.g, 1.0/256.0)*weight;
|
||||||
|
varianceb += variance_diff(mean.b - px.b, 1.0/256.0)*weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (f_pixel){
|
||||||
|
.a = variancea*(4.0/16.0),
|
||||||
|
.r = variancer*(7.0/16.0),
|
||||||
|
.g = varianceg*(9.0/16.0),
|
||||||
|
.b = varianceb*(5.0/16.0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static double box_max_error(const hist_item achv[], const struct box *box)
|
||||||
|
{
|
||||||
|
f_pixel mean = box->color;
|
||||||
|
double max_error = 0;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < box->colors; ++i) {
|
||||||
|
const double diff = colordifference(mean, achv[box->ind + i].acolor);
|
||||||
|
if (diff > max_error) {
|
||||||
|
max_error = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static double color_weight(f_pixel median, hist_item h);
|
||||||
|
|
||||||
|
static inline void hist_item_swap(hist_item *l, hist_item *r)
|
||||||
|
{
|
||||||
|
if (l != r) {
|
||||||
|
hist_item t = *l;
|
||||||
|
*l = *r;
|
||||||
|
*r = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static unsigned int qsort_pivot(const hist_item *const base, const unsigned int len);
|
||||||
|
inline static unsigned int qsort_pivot(const hist_item *const base, const unsigned int len)
|
||||||
|
{
|
||||||
|
if (len < 32) {
|
||||||
|
return len/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int aidx=8, bidx=len/2, cidx=len-1;
|
||||||
|
const unsigned int a=base[aidx].tmp.sort_value, b=base[bidx].tmp.sort_value, c=base[cidx].tmp.sort_value;
|
||||||
|
return (a < b) ? ((b < c) ? bidx : ((a < c) ? cidx : aidx ))
|
||||||
|
: ((b > c) ? bidx : ((a < c) ? aidx : cidx ));
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static unsigned int qsort_partition(hist_item *const base, const unsigned int len);
|
||||||
|
inline static unsigned int qsort_partition(hist_item *const base, const unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int l = 1, r = len;
|
||||||
|
if (len >= 8) {
|
||||||
|
hist_item_swap(&base[0], &base[qsort_pivot(base,len)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int pivot_value = base[0].tmp.sort_value;
|
||||||
|
while (l < r) {
|
||||||
|
if (base[l].tmp.sort_value >= pivot_value) {
|
||||||
|
l++;
|
||||||
|
} else {
|
||||||
|
while(l < --r && base[r].tmp.sort_value <= pivot_value) {}
|
||||||
|
hist_item_swap(&base[l], &base[r]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l--;
|
||||||
|
hist_item_swap(&base[0], &base[l]);
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** quick select algorithm */
|
||||||
|
static void hist_item_sort_range(hist_item base[], unsigned int len, unsigned int sort_start)
|
||||||
|
{
|
||||||
|
for(;;) {
|
||||||
|
const unsigned int l = qsort_partition(base, len), r = l+1;
|
||||||
|
|
||||||
|
if (l > 0 && sort_start < l) {
|
||||||
|
len = l;
|
||||||
|
}
|
||||||
|
else if (r < len && sort_start > r) {
|
||||||
|
base += r; len -= r; sort_start -= r;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** sorts array to make sum of weights lower than halfvar one side, returns edge between <halfvar and >halfvar parts of the set */
|
||||||
|
static hist_item *hist_item_sort_halfvar(hist_item base[], unsigned int len, double *const lowervar, const double halfvar)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
const unsigned int l = qsort_partition(base, len), r = l+1;
|
||||||
|
|
||||||
|
// check if sum of left side is smaller than half,
|
||||||
|
// if it is, then it doesn't need to be sorted
|
||||||
|
unsigned int t = 0; double tmpsum = *lowervar;
|
||||||
|
while (t <= l && tmpsum < halfvar) tmpsum += base[t++].color_weight;
|
||||||
|
|
||||||
|
if (tmpsum < halfvar) {
|
||||||
|
*lowervar = tmpsum;
|
||||||
|
} else {
|
||||||
|
if (l > 0) {
|
||||||
|
hist_item *res = hist_item_sort_halfvar(base, l, lowervar, halfvar);
|
||||||
|
if (res) return res;
|
||||||
|
} else {
|
||||||
|
// End of left recursion. This will be executed in order from the first element.
|
||||||
|
*lowervar += base[0].color_weight;
|
||||||
|
if (*lowervar > halfvar) return &base[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > r) {
|
||||||
|
base += r; len -= r; // tail-recursive "call"
|
||||||
|
} else {
|
||||||
|
*lowervar += base[r].color_weight;
|
||||||
|
return (*lowervar > halfvar) ? &base[r] : NULL;
|
||||||
|
}
|
||||||
|
} while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static f_pixel get_median(const struct box *b, hist_item achv[]);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int chan; float variance;
|
||||||
|
} channelvariance;
|
||||||
|
|
||||||
|
static int comparevariance(const void *ch1, const void *ch2)
|
||||||
|
{
|
||||||
|
return ((const channelvariance*)ch1)->variance > ((const channelvariance*)ch2)->variance ? -1 :
|
||||||
|
(((const channelvariance*)ch1)->variance < ((const channelvariance*)ch2)->variance ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds which channels need to be sorted first and preproceses achv for fast sort */
|
||||||
|
static double prepare_sort(struct box *b, hist_item achv[])
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** Sort dimensions by their variance, and then sort colors first by dimension with highest variance
|
||||||
|
*/
|
||||||
|
channelvariance channels[4] = {
|
||||||
|
{index_of_channel(a), b->variance.a},
|
||||||
|
{index_of_channel(r), b->variance.r},
|
||||||
|
{index_of_channel(g), b->variance.g},
|
||||||
|
{index_of_channel(b), b->variance.b},
|
||||||
|
};
|
||||||
|
|
||||||
|
qsort(channels, 4, sizeof(channels[0]), comparevariance);
|
||||||
|
|
||||||
|
const unsigned int ind1 = b->ind;
|
||||||
|
const unsigned int colors = b->colors;
|
||||||
|
#pragma omp parallel for if (colors > 25000) \
|
||||||
|
schedule(static) default(none) shared(achv, channels)
|
||||||
|
for(unsigned int i=0; i < colors; i++) {
|
||||||
|
const float *chans = (const float *)&achv[ind1 + i].acolor;
|
||||||
|
// Only the first channel really matters. When trying median cut many times
|
||||||
|
// with different histogram weights, I don't want sort randomness to influence outcome.
|
||||||
|
achv[ind1 + i].tmp.sort_value = ((unsigned int)(chans[channels[0].chan]*65535.0)<<16) |
|
||||||
|
(unsigned int)((chans[channels[2].chan] + chans[channels[1].chan]/2.0 + chans[channels[3].chan]/4.0)*65535.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const f_pixel median = get_median(b, achv);
|
||||||
|
|
||||||
|
// box will be split to make color_weight of each side even
|
||||||
|
const unsigned int ind = b->ind, end = ind+b->colors;
|
||||||
|
double totalvar = 0;
|
||||||
|
#pragma omp parallel for if (end - ind > 15000) \
|
||||||
|
schedule(static) default(shared) reduction(+:totalvar)
|
||||||
|
for(unsigned int j=ind; j < end; j++) totalvar += (achv[j].color_weight = color_weight(median, achv[j]));
|
||||||
|
return totalvar / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** finds median in unsorted set by sorting only minimum required */
|
||||||
|
static f_pixel get_median(const struct box *b, hist_item achv[])
|
||||||
|
{
|
||||||
|
const unsigned int median_start = (b->colors-1)/2;
|
||||||
|
|
||||||
|
hist_item_sort_range(&(achv[b->ind]), b->colors,
|
||||||
|
median_start);
|
||||||
|
|
||||||
|
if (b->colors&1) return achv[b->ind + median_start].acolor;
|
||||||
|
|
||||||
|
// technically the second color is not guaranteed to be sorted correctly
|
||||||
|
// but most of the time it is good enough to be useful
|
||||||
|
return averagepixels(2, &achv[b->ind + median_start]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the best splittable box. -1 if no boxes are splittable.
|
||||||
|
*/
|
||||||
|
static int best_splittable_box(struct box bv[], unsigned int boxes, const double max_mse)
|
||||||
|
{
|
||||||
|
int bi=-1; double maxsum=0;
|
||||||
|
for(unsigned int i=0; i < boxes; i++) {
|
||||||
|
if (bv[i].colors < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// looks only at max variance, because it's only going to split by it
|
||||||
|
const double cv = MAX(bv[i].variance.r, MAX(bv[i].variance.g,bv[i].variance.b));
|
||||||
|
double thissum = bv[i].sum * MAX(bv[i].variance.a, cv);
|
||||||
|
|
||||||
|
if (bv[i].max_error > max_mse) {
|
||||||
|
thissum = thissum* bv[i].max_error/max_mse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thissum > maxsum) {
|
||||||
|
maxsum = thissum;
|
||||||
|
bi = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bi;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static double color_weight(f_pixel median, hist_item h)
|
||||||
|
{
|
||||||
|
float diff = colordifference(median, h.acolor);
|
||||||
|
return sqrt(diff) * (sqrt(1.0+h.adjusted_weight)-1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_colormap_from_boxes(colormap *map, struct box bv[], unsigned int boxes, hist_item *achv);
|
||||||
|
static void adjust_histogram(hist_item *achv, const struct box bv[], unsigned int boxes);
|
||||||
|
|
||||||
|
static double box_error(const struct box *box, const hist_item achv[])
|
||||||
|
{
|
||||||
|
f_pixel avg = box->color;
|
||||||
|
|
||||||
|
double total_error=0;
|
||||||
|
for (unsigned int i = 0; i < box->colors; ++i) {
|
||||||
|
total_error += colordifference(avg, achv[box->ind + i].acolor) * achv[box->ind + i].perceptual_weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool total_box_error_below_target(double target_mse, struct box bv[], unsigned int boxes, const histogram *hist)
|
||||||
|
{
|
||||||
|
target_mse *= hist->total_perceptual_weight;
|
||||||
|
double total_error=0;
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < boxes; i++) {
|
||||||
|
// error is (re)calculated lazily
|
||||||
|
if (bv[i].total_error >= 0) {
|
||||||
|
total_error += bv[i].total_error;
|
||||||
|
}
|
||||||
|
if (total_error > target_mse) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < boxes; i++) {
|
||||||
|
if (bv[i].total_error < 0) {
|
||||||
|
bv[i].total_error = box_error(&bv[i], hist->achv);
|
||||||
|
total_error += bv[i].total_error;
|
||||||
|
}
|
||||||
|
if (total_error > target_mse) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void box_init(struct box *box, const hist_item *achv, const unsigned int ind, const unsigned int colors, const double sum) {
|
||||||
|
box->ind = ind;
|
||||||
|
box->colors = colors;
|
||||||
|
box->sum = sum;
|
||||||
|
box->total_error = -1;
|
||||||
|
|
||||||
|
box->color = averagepixels(colors, &achv[ind]);
|
||||||
|
#pragma omp task if (colors > 5000)
|
||||||
|
box->variance = box_variance(achv, box);
|
||||||
|
#pragma omp task if (colors > 8000)
|
||||||
|
box->max_error = box_max_error(achv, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Here is the fun part, the median-cut colormap generator. This is based
|
||||||
|
** on Paul Heckbert's paper, "Color Image Quantization for Frame Buffer
|
||||||
|
** Display," SIGGRAPH 1982 Proceedings, page 297.
|
||||||
|
*/
|
||||||
|
LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const double target_mse, const double max_mse, void* (*malloc)(size_t), void (*free)(void*))
|
||||||
|
{
|
||||||
|
hist_item *achv = hist->achv;
|
||||||
|
LIQ_ARRAY(struct box, bv, newcolors);
|
||||||
|
unsigned int boxes = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Set up the initial box.
|
||||||
|
*/
|
||||||
|
#pragma omp parallel
|
||||||
|
#pragma omp single
|
||||||
|
{
|
||||||
|
double sum = 0;
|
||||||
|
for(unsigned int i=0; i < hist->size; i++) {
|
||||||
|
sum += achv[i].adjusted_weight;
|
||||||
|
}
|
||||||
|
#pragma omp taskgroup
|
||||||
|
{
|
||||||
|
box_init(&bv[0], achv, 0, hist->size, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Main loop: split boxes until we have enough.
|
||||||
|
*/
|
||||||
|
while (boxes < newcolors) {
|
||||||
|
|
||||||
|
// first splits boxes that exceed quality limit (to have colors for things like odd green pixel),
|
||||||
|
// later raises the limit to allow large smooth areas/gradients get colors.
|
||||||
|
const double current_max_mse = max_mse + (boxes/(double)newcolors)*16.0*max_mse;
|
||||||
|
const int bi = best_splittable_box(bv, boxes, current_max_mse);
|
||||||
|
if (bi < 0) {
|
||||||
|
break; /* ran out of colors! */
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int indx = bv[bi].ind;
|
||||||
|
unsigned int clrs = bv[bi].colors;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Classic implementation tries to get even number of colors or pixels in each subdivision.
|
||||||
|
|
||||||
|
Here, instead of popularity I use (sqrt(popularity)*variance) metric.
|
||||||
|
Each subdivision balances number of pixels (popular colors) and low variance -
|
||||||
|
boxes can be large if they have similar colors. Later boxes with high variance
|
||||||
|
will be more likely to be split.
|
||||||
|
|
||||||
|
Median used as expected value gives much better results than mean.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const double halfvar = prepare_sort(&bv[bi], achv);
|
||||||
|
double lowervar=0;
|
||||||
|
|
||||||
|
// hist_item_sort_halfvar sorts and sums lowervar at the same time
|
||||||
|
// returns item to break at …minus one, which does smell like an off-by-one error.
|
||||||
|
hist_item *break_p = hist_item_sort_halfvar(&achv[indx], clrs, &lowervar, halfvar);
|
||||||
|
unsigned int break_at = MIN(clrs-1, break_p - &achv[indx] + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Split the box.
|
||||||
|
*/
|
||||||
|
double sm = bv[bi].sum;
|
||||||
|
double lowersum = 0;
|
||||||
|
for(unsigned int i=0; i < break_at; i++) lowersum += achv[indx + i].adjusted_weight;
|
||||||
|
|
||||||
|
#pragma omp taskgroup
|
||||||
|
{
|
||||||
|
box_init(&bv[bi], achv, indx, break_at, lowersum);
|
||||||
|
box_init(&bv[boxes], achv, indx + break_at, clrs - break_at, sm - lowersum);
|
||||||
|
}
|
||||||
|
|
||||||
|
++boxes;
|
||||||
|
|
||||||
|
if (total_box_error_below_target(target_mse, bv, boxes, hist)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colormap *map = pam_colormap(boxes, malloc, free);
|
||||||
|
set_colormap_from_boxes(map, bv, boxes, achv);
|
||||||
|
|
||||||
|
adjust_histogram(achv, bv, boxes);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_colormap_from_boxes(colormap *map, struct box* bv, unsigned int boxes, hist_item *achv)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** Ok, we've got enough boxes. Now choose a representative color for
|
||||||
|
** each box. There are a number of possible ways to make this choice.
|
||||||
|
** One would be to choose the center of the box; this ignores any structure
|
||||||
|
** within the boxes. Another method would be to average all the colors in
|
||||||
|
** the box - this is the method specified in Heckbert's paper.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(unsigned int bi = 0; bi < boxes; ++bi) {
|
||||||
|
map->palette[bi].acolor = bv[bi].color;
|
||||||
|
|
||||||
|
/* store total color popularity (perceptual_weight is approximation of it) */
|
||||||
|
map->palette[bi].popularity = 0;
|
||||||
|
for(unsigned int i=bv[bi].ind; i < bv[bi].ind+bv[bi].colors; i++) {
|
||||||
|
map->palette[bi].popularity += achv[i].perceptual_weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase histogram popularity by difference from the final color (this is used as part of feedback loop) */
|
||||||
|
static void adjust_histogram(hist_item *achv, const struct box* bv, unsigned int boxes)
|
||||||
|
{
|
||||||
|
for(unsigned int bi = 0; bi < boxes; ++bi) {
|
||||||
|
for(unsigned int i=bv[bi].ind; i < bv[bi].ind+bv[bi].colors; i++) {
|
||||||
|
achv[i].tmp.likely_colormap_index = bi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static f_pixel averagepixels(unsigned int clrs, const hist_item achv[])
|
||||||
|
{
|
||||||
|
double r = 0, g = 0, b = 0, a = 0, sum = 0;
|
||||||
|
|
||||||
|
#pragma omp parallel for if (clrs > 25000) \
|
||||||
|
schedule(static) default(shared) reduction(+:a) reduction(+:r) reduction(+:g) reduction(+:b) reduction(+:sum)
|
||||||
|
for(unsigned int i = 0; i < clrs; i++) {
|
||||||
|
const f_pixel px = achv[i].acolor;
|
||||||
|
const double weight = achv[i].adjusted_weight;
|
||||||
|
|
||||||
|
sum += weight;
|
||||||
|
a += px.a * weight;
|
||||||
|
r += px.r * weight;
|
||||||
|
g += px.g * weight;
|
||||||
|
b += px.b * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sum) {
|
||||||
|
a /= sum;
|
||||||
|
r /= sum;
|
||||||
|
g /= sum;
|
||||||
|
b /= sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!isnan(r) && !isnan(g) && !isnan(b) && !isnan(a));
|
||||||
|
|
||||||
|
return (f_pixel){.r=r, .g=g, .b=b, .a=a};
|
||||||
|
}
|
||||||
2
vendor/code.ivysaur.me/imagequant/mediancut.h
generated
vendored
Normal file
2
vendor/code.ivysaur.me/imagequant/mediancut.h
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const double target_mse, const double max_mse, void* (*malloc)(size_t), void (*free)(void*));
|
||||||
70
vendor/code.ivysaur.me/imagequant/mempool.c
generated
vendored
Normal file
70
vendor/code.ivysaur.me/imagequant/mempool.c
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
** © 2009-2017 by Kornel Lesiński.
|
||||||
|
** © 1989, 1991 by Jef Poskanzer.
|
||||||
|
** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider.
|
||||||
|
**
|
||||||
|
** See COPYRIGHT file for license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "mempool.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ALIGN_MASK 15UL
|
||||||
|
#define MEMPOOL_RESERVED ((sizeof(struct mempool)+ALIGN_MASK) & ~ALIGN_MASK)
|
||||||
|
|
||||||
|
struct mempool {
|
||||||
|
unsigned int used, size;
|
||||||
|
void* (*malloc)(size_t);
|
||||||
|
void (*free)(void*);
|
||||||
|
struct mempool *next;
|
||||||
|
};
|
||||||
|
LIQ_PRIVATE void* mempool_create(mempoolptr *mptr, const unsigned int size, unsigned int max_size, void* (*malloc)(size_t), void (*free)(void*))
|
||||||
|
{
|
||||||
|
if (*mptr && ((*mptr)->used+size) <= (*mptr)->size) {
|
||||||
|
unsigned int prevused = (*mptr)->used;
|
||||||
|
(*mptr)->used += (size+15UL) & ~0xFUL;
|
||||||
|
return ((char*)(*mptr)) + prevused;
|
||||||
|
}
|
||||||
|
|
||||||
|
mempoolptr old = *mptr;
|
||||||
|
if (!max_size) max_size = (1<<17);
|
||||||
|
max_size = size+ALIGN_MASK > max_size ? size+ALIGN_MASK : max_size;
|
||||||
|
|
||||||
|
*mptr = malloc(MEMPOOL_RESERVED + max_size);
|
||||||
|
if (!*mptr) return NULL;
|
||||||
|
**mptr = (struct mempool){
|
||||||
|
.malloc = malloc,
|
||||||
|
.free = free,
|
||||||
|
.size = MEMPOOL_RESERVED + max_size,
|
||||||
|
.used = sizeof(struct mempool),
|
||||||
|
.next = old,
|
||||||
|
};
|
||||||
|
uintptr_t mptr_used_start = (uintptr_t)(*mptr) + (*mptr)->used;
|
||||||
|
(*mptr)->used += (ALIGN_MASK + 1 - (mptr_used_start & ALIGN_MASK)) & ALIGN_MASK; // reserve bytes required to make subsequent allocations aligned
|
||||||
|
assert(!(((uintptr_t)(*mptr) + (*mptr)->used) & ALIGN_MASK));
|
||||||
|
|
||||||
|
return mempool_alloc(mptr, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void* mempool_alloc(mempoolptr *mptr, const unsigned int size, const unsigned int max_size)
|
||||||
|
{
|
||||||
|
if (((*mptr)->used+size) <= (*mptr)->size) {
|
||||||
|
unsigned int prevused = (*mptr)->used;
|
||||||
|
(*mptr)->used += (size + ALIGN_MASK) & ~ALIGN_MASK;
|
||||||
|
return ((char*)(*mptr)) + prevused;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mempool_create(mptr, size, max_size, (*mptr)->malloc, (*mptr)->free);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void mempool_destroy(mempoolptr m)
|
||||||
|
{
|
||||||
|
while (m) {
|
||||||
|
mempoolptr next = m->next;
|
||||||
|
m->free(m);
|
||||||
|
m = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
vendor/code.ivysaur.me/imagequant/mempool.h
generated
vendored
Normal file
13
vendor/code.ivysaur.me/imagequant/mempool.h
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef MEMPOOL_H
|
||||||
|
#define MEMPOOL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct mempool;
|
||||||
|
typedef struct mempool *mempoolptr;
|
||||||
|
|
||||||
|
LIQ_PRIVATE void* mempool_create(mempoolptr *mptr, const unsigned int size, unsigned int capacity, void* (*malloc)(size_t), void (*free)(void*));
|
||||||
|
LIQ_PRIVATE void* mempool_alloc(mempoolptr *mptr, const unsigned int size, const unsigned int capacity);
|
||||||
|
LIQ_PRIVATE void mempool_destroy(mempoolptr m);
|
||||||
|
|
||||||
|
#endif
|
||||||
195
vendor/code.ivysaur.me/imagequant/nearest.c
generated
vendored
Normal file
195
vendor/code.ivysaur.me/imagequant/nearest.c
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
** © 2009-2015 by Kornel Lesiński.
|
||||||
|
** © 1989, 1991 by Jef Poskanzer.
|
||||||
|
** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider.
|
||||||
|
**
|
||||||
|
** See COPYRIGHT file for license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "pam.h"
|
||||||
|
#include "nearest.h"
|
||||||
|
#include "mempool.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct vp_sort_tmp {
|
||||||
|
float distance_squared;
|
||||||
|
unsigned int idx;
|
||||||
|
} vp_sort_tmp;
|
||||||
|
|
||||||
|
typedef struct vp_search_tmp {
|
||||||
|
float distance;
|
||||||
|
unsigned int idx;
|
||||||
|
int exclude;
|
||||||
|
} vp_search_tmp;
|
||||||
|
|
||||||
|
typedef struct vp_node {
|
||||||
|
struct vp_node *near, *far;
|
||||||
|
f_pixel vantage_point;
|
||||||
|
float radius;
|
||||||
|
unsigned int idx;
|
||||||
|
} vp_node;
|
||||||
|
|
||||||
|
struct nearest_map {
|
||||||
|
vp_node *root;
|
||||||
|
const colormap_item *palette;
|
||||||
|
float nearest_other_color_dist[256];
|
||||||
|
mempoolptr mempool;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vp_search_node(const vp_node *node, const f_pixel *const needle, vp_search_tmp *const best_candidate);
|
||||||
|
|
||||||
|
static int vp_compare_distance(const void *ap, const void *bp) {
|
||||||
|
float a = ((const vp_sort_tmp*)ap)->distance_squared;
|
||||||
|
float b = ((const vp_sort_tmp*)bp)->distance_squared;
|
||||||
|
return a > b ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vp_sort_indexes_by_distance(const f_pixel vantage_point, vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) {
|
||||||
|
for(int i=0; i < num_indexes; i++) {
|
||||||
|
indexes[i].distance_squared = colordifference(vantage_point, items[indexes[i].idx].acolor);
|
||||||
|
}
|
||||||
|
qsort(indexes, num_indexes, sizeof(indexes[0]), vp_compare_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Usually it should pick farthest point, but picking most popular point seems to make search quicker anyway
|
||||||
|
*/
|
||||||
|
static int vp_find_best_vantage_point_index(vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) {
|
||||||
|
int best = 0;
|
||||||
|
float best_popularity = items[indexes[0].idx].popularity;
|
||||||
|
for(int i = 1; i < num_indexes; i++) {
|
||||||
|
if (items[indexes[i].idx].popularity > best_popularity) {
|
||||||
|
best_popularity = items[indexes[i].idx].popularity;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vp_node *vp_create_node(mempoolptr *m, vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) {
|
||||||
|
if (num_indexes <= 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vp_node *node = mempool_alloc(m, sizeof(node[0]), 0);
|
||||||
|
|
||||||
|
if (num_indexes == 1) {
|
||||||
|
*node = (vp_node){
|
||||||
|
.vantage_point = items[indexes[0].idx].acolor,
|
||||||
|
.idx = indexes[0].idx,
|
||||||
|
.radius = MAX_DIFF,
|
||||||
|
};
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int ref = vp_find_best_vantage_point_index(indexes, num_indexes, items);
|
||||||
|
const int ref_idx = indexes[ref].idx;
|
||||||
|
|
||||||
|
// Removes the `ref_idx` item from remaining items, because it's included in the current node
|
||||||
|
num_indexes -= 1;
|
||||||
|
indexes[ref] = indexes[num_indexes];
|
||||||
|
|
||||||
|
vp_sort_indexes_by_distance(items[ref_idx].acolor, indexes, num_indexes, items);
|
||||||
|
|
||||||
|
// Remaining items are split by the median distance
|
||||||
|
const int half_idx = num_indexes/2;
|
||||||
|
|
||||||
|
*node = (vp_node){
|
||||||
|
.vantage_point = items[ref_idx].acolor,
|
||||||
|
.idx = ref_idx,
|
||||||
|
.radius = sqrtf(indexes[half_idx].distance_squared),
|
||||||
|
};
|
||||||
|
node->near = vp_create_node(m, indexes, half_idx, items);
|
||||||
|
node->far = vp_create_node(m, &indexes[half_idx], num_indexes - half_idx, items);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *map) {
|
||||||
|
mempoolptr m = NULL;
|
||||||
|
struct nearest_map *handle = mempool_create(&m, sizeof(handle[0]), sizeof(handle[0]) + sizeof(vp_node)*map->colors+16, map->malloc, map->free);
|
||||||
|
|
||||||
|
LIQ_ARRAY(vp_sort_tmp, indexes, map->colors);
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < map->colors; i++) {
|
||||||
|
indexes[i].idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
vp_node *root = vp_create_node(&m, indexes, map->colors, map->palette);
|
||||||
|
*handle = (struct nearest_map){
|
||||||
|
.root = root,
|
||||||
|
.palette = map->palette,
|
||||||
|
.mempool = m,
|
||||||
|
};
|
||||||
|
|
||||||
|
for(unsigned int i=0; i < map->colors; i++) {
|
||||||
|
vp_search_tmp best = {
|
||||||
|
.distance = MAX_DIFF,
|
||||||
|
.exclude = i,
|
||||||
|
};
|
||||||
|
vp_search_node(root, &map->palette[i].acolor, &best);
|
||||||
|
handle->nearest_other_color_dist[i] = best.distance * best.distance / 4.0; // half of squared distance
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vp_search_node(const vp_node *node, const f_pixel *const needle, vp_search_tmp *const best_candidate) {
|
||||||
|
do {
|
||||||
|
const float distance = sqrtf(colordifference(node->vantage_point, *needle));
|
||||||
|
|
||||||
|
if (distance < best_candidate->distance && best_candidate->exclude != node->idx) {
|
||||||
|
best_candidate->distance = distance;
|
||||||
|
best_candidate->idx = node->idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse towards most likely candidate first to narrow best candidate's distance as soon as possible
|
||||||
|
if (distance < node->radius) {
|
||||||
|
if (node->near) {
|
||||||
|
vp_search_node(node->near, needle, best_candidate);
|
||||||
|
}
|
||||||
|
// The best node (final answer) may be just ouside the radius, but not farther than
|
||||||
|
// the best distance we know so far. The vp_search_node above should have narrowed
|
||||||
|
// best_candidate->distance, so this path is rarely taken.
|
||||||
|
if (node->far && distance >= node->radius - best_candidate->distance) {
|
||||||
|
node = node->far; // Fast tail recursion
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (node->far) {
|
||||||
|
vp_search_node(node->far, needle, best_candidate);
|
||||||
|
}
|
||||||
|
if (node->near && distance <= node->radius + best_candidate->distance) {
|
||||||
|
node = node->near; // Fast tail recursion
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE unsigned int nearest_search(const struct nearest_map *handle, const f_pixel *px, const int likely_colormap_index, float *diff) {
|
||||||
|
const float guess_diff = colordifference(handle->palette[likely_colormap_index].acolor, *px);
|
||||||
|
if (guess_diff < handle->nearest_other_color_dist[likely_colormap_index]) {
|
||||||
|
if (diff) *diff = guess_diff;
|
||||||
|
return likely_colormap_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
vp_search_tmp best_candidate = {
|
||||||
|
.distance = sqrtf(guess_diff),
|
||||||
|
.idx = likely_colormap_index,
|
||||||
|
.exclude = -1,
|
||||||
|
};
|
||||||
|
vp_search_node(handle->root, px, &best_candidate);
|
||||||
|
if (diff) {
|
||||||
|
*diff = best_candidate.distance * best_candidate.distance;
|
||||||
|
}
|
||||||
|
return best_candidate.idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void nearest_free(struct nearest_map *centroids)
|
||||||
|
{
|
||||||
|
mempool_destroy(centroids->mempool);
|
||||||
|
}
|
||||||
8
vendor/code.ivysaur.me/imagequant/nearest.h
generated
vendored
Normal file
8
vendor/code.ivysaur.me/imagequant/nearest.h
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//
|
||||||
|
// nearest.h
|
||||||
|
// pngquant
|
||||||
|
//
|
||||||
|
struct nearest_map;
|
||||||
|
LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *palette);
|
||||||
|
LIQ_PRIVATE unsigned int nearest_search(const struct nearest_map *map, const f_pixel *px, const int palette_index_guess, float *diff);
|
||||||
|
LIQ_PRIVATE void nearest_free(struct nearest_map *map);
|
||||||
286
vendor/code.ivysaur.me/imagequant/pam.c
generated
vendored
Normal file
286
vendor/code.ivysaur.me/imagequant/pam.c
generated
vendored
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
/* pam.c - pam (portable alpha map) utility library
|
||||||
|
**
|
||||||
|
** © 2009-2017 by Kornel Lesiński.
|
||||||
|
** © 1989, 1991 by Jef Poskanzer.
|
||||||
|
** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider.
|
||||||
|
**
|
||||||
|
** See COPYRIGHT file for license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "libimagequant.h"
|
||||||
|
#include "pam.h"
|
||||||
|
#include "mempool.h"
|
||||||
|
|
||||||
|
LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba_pixel *const pixels[], unsigned int cols, unsigned int rows, const unsigned char *importance_map)
|
||||||
|
{
|
||||||
|
const unsigned int ignorebits = acht->ignorebits;
|
||||||
|
const unsigned int channel_mask = 255U>>ignorebits<<ignorebits;
|
||||||
|
const unsigned int channel_hmask = (255U>>ignorebits) ^ 0xFFU;
|
||||||
|
const unsigned int posterize_mask = channel_mask << 24 | channel_mask << 16 | channel_mask << 8 | channel_mask;
|
||||||
|
const unsigned int posterize_high_mask = channel_hmask << 24 | channel_hmask << 16 | channel_hmask << 8 | channel_hmask;
|
||||||
|
|
||||||
|
const unsigned int hash_size = acht->hash_size;
|
||||||
|
|
||||||
|
/* Go through the entire image, building a hash table of colors. */
|
||||||
|
for(unsigned int row = 0; row < rows; ++row) {
|
||||||
|
|
||||||
|
for(unsigned int col = 0; col < cols; ++col) {
|
||||||
|
unsigned int boost;
|
||||||
|
|
||||||
|
// RGBA color is casted to long for easier hasing/comparisons
|
||||||
|
union rgba_as_int px = {pixels[row][col]};
|
||||||
|
unsigned int hash;
|
||||||
|
if (!px.rgba.a) {
|
||||||
|
// "dirty alpha" has different RGBA values that end up being the same fully transparent color
|
||||||
|
px.l=0; hash=0;
|
||||||
|
|
||||||
|
boost = 2000;
|
||||||
|
if (importance_map) {
|
||||||
|
importance_map++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// mask posterizes all 4 channels in one go
|
||||||
|
px.l = (px.l & posterize_mask) | ((px.l & posterize_high_mask) >> (8-ignorebits));
|
||||||
|
// fancier hashing algorithms didn't improve much
|
||||||
|
hash = px.l % hash_size;
|
||||||
|
|
||||||
|
if (importance_map) {
|
||||||
|
boost = *importance_map++;
|
||||||
|
} else {
|
||||||
|
boost = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pam_add_to_hash(acht, hash, boost, px, row, rows)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
acht->cols = cols;
|
||||||
|
acht->rows += rows;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE bool pam_add_to_hash(struct acolorhash_table *acht, unsigned int hash, unsigned int boost, union rgba_as_int px, unsigned int row, unsigned int rows)
|
||||||
|
{
|
||||||
|
/* head of the hash function stores first 2 colors inline (achl->used = 1..2),
|
||||||
|
to reduce number of allocations of achl->other_items.
|
||||||
|
*/
|
||||||
|
struct acolorhist_arr_head *achl = &acht->buckets[hash];
|
||||||
|
if (achl->inline1.color.l == px.l && achl->used) {
|
||||||
|
achl->inline1.perceptual_weight += boost;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (achl->used) {
|
||||||
|
if (achl->used > 1) {
|
||||||
|
if (achl->inline2.color.l == px.l) {
|
||||||
|
achl->inline2.perceptual_weight += boost;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// other items are stored as an array (which gets reallocated if needed)
|
||||||
|
struct acolorhist_arr_item *other_items = achl->other_items;
|
||||||
|
unsigned int i = 0;
|
||||||
|
for (; i < achl->used-2; i++) {
|
||||||
|
if (other_items[i].color.l == px.l) {
|
||||||
|
other_items[i].perceptual_weight += boost;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the array was allocated with spare items
|
||||||
|
if (i < achl->capacity) {
|
||||||
|
other_items[i] = (struct acolorhist_arr_item){
|
||||||
|
.color = px,
|
||||||
|
.perceptual_weight = boost,
|
||||||
|
};
|
||||||
|
achl->used++;
|
||||||
|
++acht->colors;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++acht->colors > acht->maxcolors) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct acolorhist_arr_item *new_items;
|
||||||
|
unsigned int capacity;
|
||||||
|
if (!other_items) { // there was no array previously, alloc "small" array
|
||||||
|
capacity = 8;
|
||||||
|
if (acht->freestackp <= 0) {
|
||||||
|
// estimate how many colors are going to be + headroom
|
||||||
|
const size_t mempool_size = ((acht->rows + rows-row) * 2 * acht->colors / (acht->rows + row + 1) + 1024) * sizeof(struct acolorhist_arr_item);
|
||||||
|
new_items = mempool_alloc(&acht->mempool, sizeof(struct acolorhist_arr_item)*capacity, mempool_size);
|
||||||
|
} else {
|
||||||
|
// freestack stores previously freed (reallocated) arrays that can be reused
|
||||||
|
// (all pesimistically assumed to be capacity = 8)
|
||||||
|
new_items = acht->freestack[--acht->freestackp];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const unsigned int stacksize = sizeof(acht->freestack)/sizeof(acht->freestack[0]);
|
||||||
|
|
||||||
|
// simply reallocs and copies array to larger capacity
|
||||||
|
capacity = achl->capacity*2 + 16;
|
||||||
|
if (acht->freestackp < stacksize-1) {
|
||||||
|
acht->freestack[acht->freestackp++] = other_items;
|
||||||
|
}
|
||||||
|
const size_t mempool_size = ((acht->rows + rows-row) * 2 * acht->colors / (acht->rows + row + 1) + 32*capacity) * sizeof(struct acolorhist_arr_item);
|
||||||
|
new_items = mempool_alloc(&acht->mempool, sizeof(struct acolorhist_arr_item)*capacity, mempool_size);
|
||||||
|
if (!new_items) return false;
|
||||||
|
memcpy(new_items, other_items, sizeof(other_items[0])*achl->capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
achl->other_items = new_items;
|
||||||
|
achl->capacity = capacity;
|
||||||
|
new_items[i] = (struct acolorhist_arr_item){
|
||||||
|
.color = px,
|
||||||
|
.perceptual_weight = boost,
|
||||||
|
};
|
||||||
|
achl->used++;
|
||||||
|
} else {
|
||||||
|
// these are elses for first checks whether first and second inline-stored colors are used
|
||||||
|
achl->inline2.color.l = px.l;
|
||||||
|
achl->inline2.perceptual_weight = boost;
|
||||||
|
achl->used = 2;
|
||||||
|
++acht->colors;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
achl->inline1.color.l = px.l;
|
||||||
|
achl->inline1.perceptual_weight = boost;
|
||||||
|
achl->used = 1;
|
||||||
|
++acht->colors;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE struct acolorhash_table *pam_allocacolorhash(unsigned int maxcolors, unsigned int surface, unsigned int ignorebits, void* (*malloc)(size_t), void (*free)(void*))
|
||||||
|
{
|
||||||
|
const size_t estimated_colors = MIN(maxcolors, surface/(ignorebits + (surface > 512*512 ? 6 : 5)));
|
||||||
|
const size_t hash_size = estimated_colors < 66000 ? 6673 : (estimated_colors < 200000 ? 12011 : 24019);
|
||||||
|
|
||||||
|
mempoolptr m = NULL;
|
||||||
|
const size_t buckets_size = hash_size * sizeof(struct acolorhist_arr_head);
|
||||||
|
const size_t mempool_size = sizeof(struct acolorhash_table) + buckets_size + estimated_colors * sizeof(struct acolorhist_arr_item);
|
||||||
|
struct acolorhash_table *t = mempool_create(&m, sizeof(*t) + buckets_size, mempool_size, malloc, free);
|
||||||
|
if (!t) return NULL;
|
||||||
|
*t = (struct acolorhash_table){
|
||||||
|
.mempool = m,
|
||||||
|
.hash_size = hash_size,
|
||||||
|
.maxcolors = maxcolors,
|
||||||
|
.ignorebits = ignorebits,
|
||||||
|
};
|
||||||
|
memset(t->buckets, 0, buckets_size);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static float pam_add_to_hist(const float *gamma_lut, hist_item *achv, unsigned int *j, const struct acolorhist_arr_item *entry, const float max_perceptual_weight)
|
||||||
|
{
|
||||||
|
if (entry->perceptual_weight == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const float w = MIN(entry->perceptual_weight/128.f, max_perceptual_weight);
|
||||||
|
achv[*j].adjusted_weight = achv[*j].perceptual_weight = w;
|
||||||
|
achv[*j].acolor = rgba_to_f(gamma_lut, entry->color.rgba);
|
||||||
|
*j += 1;
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table *acht, const double gamma, void* (*malloc)(size_t), void (*free)(void*))
|
||||||
|
{
|
||||||
|
histogram *hist = malloc(sizeof(hist[0]));
|
||||||
|
if (!hist || !acht) return NULL;
|
||||||
|
*hist = (histogram){
|
||||||
|
.achv = malloc(MAX(1,acht->colors) * sizeof(hist->achv[0])),
|
||||||
|
.size = acht->colors,
|
||||||
|
.free = free,
|
||||||
|
.ignorebits = acht->ignorebits,
|
||||||
|
};
|
||||||
|
if (!hist->achv) return NULL;
|
||||||
|
|
||||||
|
float gamma_lut[256];
|
||||||
|
to_f_set_gamma(gamma_lut, gamma);
|
||||||
|
|
||||||
|
/* Limit perceptual weight to 1/10th of the image surface area to prevent
|
||||||
|
a single color from dominating all others. */
|
||||||
|
float max_perceptual_weight = 0.1f * acht->cols * acht->rows;
|
||||||
|
double total_weight = 0;
|
||||||
|
|
||||||
|
unsigned int j=0;
|
||||||
|
for(unsigned int i=0; i < acht->hash_size; ++i) {
|
||||||
|
const struct acolorhist_arr_head *const achl = &acht->buckets[i];
|
||||||
|
if (achl->used) {
|
||||||
|
total_weight += pam_add_to_hist(gamma_lut, hist->achv, &j, &achl->inline1, max_perceptual_weight);
|
||||||
|
|
||||||
|
if (achl->used > 1) {
|
||||||
|
total_weight += pam_add_to_hist(gamma_lut, hist->achv, &j, &achl->inline2, max_perceptual_weight);
|
||||||
|
|
||||||
|
for(unsigned int k=0; k < achl->used-2; k++) {
|
||||||
|
total_weight += pam_add_to_hist(gamma_lut, hist->achv, &j, &achl->other_items[k], max_perceptual_weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hist->size = j;
|
||||||
|
hist->total_perceptual_weight = total_weight;
|
||||||
|
if (!j) {
|
||||||
|
pam_freeacolorhist(hist);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return hist;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LIQ_PRIVATE void pam_freeacolorhash(struct acolorhash_table *acht)
|
||||||
|
{
|
||||||
|
if (acht) {
|
||||||
|
mempool_destroy(acht->mempool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void pam_freeacolorhist(histogram *hist)
|
||||||
|
{
|
||||||
|
hist->free(hist->achv);
|
||||||
|
hist->free(hist);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE colormap *pam_colormap(unsigned int colors, void* (*malloc)(size_t), void (*free)(void*))
|
||||||
|
{
|
||||||
|
assert(colors > 0 && colors < 65536);
|
||||||
|
|
||||||
|
colormap *map;
|
||||||
|
const size_t colors_size = colors * sizeof(map->palette[0]);
|
||||||
|
map = malloc(sizeof(colormap) + colors_size);
|
||||||
|
if (!map) return NULL;
|
||||||
|
*map = (colormap){
|
||||||
|
.malloc = malloc,
|
||||||
|
.free = free,
|
||||||
|
.colors = colors,
|
||||||
|
};
|
||||||
|
memset(map->palette, 0, colors_size);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE colormap *pam_duplicate_colormap(colormap *map)
|
||||||
|
{
|
||||||
|
colormap *dupe = pam_colormap(map->colors, map->malloc, map->free);
|
||||||
|
for(unsigned int i=0; i < map->colors; i++) {
|
||||||
|
dupe->palette[i] = map->palette[i];
|
||||||
|
}
|
||||||
|
return dupe;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void pam_freecolormap(colormap *c)
|
||||||
|
{
|
||||||
|
c->free(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIQ_PRIVATE void to_f_set_gamma(float gamma_lut[], const double gamma)
|
||||||
|
{
|
||||||
|
for(int i=0; i < 256; i++) {
|
||||||
|
gamma_lut[i] = pow((double)i/255.0, internal_gamma/gamma);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
277
vendor/code.ivysaur.me/imagequant/pam.h
generated
vendored
Normal file
277
vendor/code.ivysaur.me/imagequant/pam.h
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
/* pam.h - pam (portable alpha map) utility library
|
||||||
|
**
|
||||||
|
** Colormap routines.
|
||||||
|
**
|
||||||
|
** Copyright (C) 1989, 1991 by Jef Poskanzer.
|
||||||
|
** Copyright (C) 1997 by Greg Roelofs.
|
||||||
|
**
|
||||||
|
** Permission to use, copy, modify, and distribute this software and its
|
||||||
|
** documentation for any purpose and without fee is hereby granted, provided
|
||||||
|
** that the above copyright notice appear in all copies and that both that
|
||||||
|
** copyright notice and this permission notice appear in supporting
|
||||||
|
** documentation. This software is provided "as is" without express or
|
||||||
|
** implied warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PAM_H
|
||||||
|
#define PAM_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
|
# define MAX(a,b) ((a) > (b)? (a) : (b))
|
||||||
|
# define MIN(a,b) ((a) < (b)? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_DIFF 1e20
|
||||||
|
|
||||||
|
#ifndef USE_SSE
|
||||||
|
# if defined(__SSE__) && (defined(__amd64__) || defined(__X86_64__) || defined(_WIN64) || defined(WIN32) || defined(__WIN32__))
|
||||||
|
# define USE_SSE 1
|
||||||
|
# else
|
||||||
|
# define USE_SSE 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_SSE
|
||||||
|
# include <xmmintrin.h>
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
# include <intrin.h>
|
||||||
|
# define SSE_ALIGN
|
||||||
|
# else
|
||||||
|
# define SSE_ALIGN __attribute__ ((aligned (16)))
|
||||||
|
# if defined(__i386__) && defined(__PIC__)
|
||||||
|
# define cpuid(func,ax,bx,cx,dx)\
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"push %%ebx\n" \
|
||||||
|
"cpuid\n" \
|
||||||
|
"mov %%ebx, %1\n" \
|
||||||
|
"pop %%ebx\n" \
|
||||||
|
: "=a" (ax), "=r" (bx), "=c" (cx), "=d" (dx) \
|
||||||
|
: "a" (func));
|
||||||
|
# else
|
||||||
|
# define cpuid(func,ax,bx,cx,dx)\
|
||||||
|
__asm__ __volatile__ ("cpuid":\
|
||||||
|
"=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
# define SSE_ALIGN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#define LIQ_ARRAY(type, var, count) type var[count]
|
||||||
|
#else
|
||||||
|
#define LIQ_ARRAY(type, var, count) type* var = (type*)_alloca(sizeof(type)*(count))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined (__llvm__)
|
||||||
|
#define ALWAYS_INLINE __attribute__((always_inline)) inline
|
||||||
|
#define NEVER_INLINE __attribute__ ((noinline))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define inline __inline
|
||||||
|
#define restrict __restrict
|
||||||
|
#define ALWAYS_INLINE __forceinline
|
||||||
|
#define NEVER_INLINE __declspec(noinline)
|
||||||
|
#else
|
||||||
|
#define ALWAYS_INLINE inline
|
||||||
|
#define NEVER_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* from pam.h */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char r, g, b, a;
|
||||||
|
} rgba_pixel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float a, r, g, b;
|
||||||
|
} SSE_ALIGN f_pixel;
|
||||||
|
|
||||||
|
static const float internal_gamma = 0.5499f;
|
||||||
|
|
||||||
|
LIQ_PRIVATE void to_f_set_gamma(float gamma_lut[], const double gamma);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts 8-bit color to internal gamma and premultiplied alpha.
|
||||||
|
(premultiplied color space is much better for blending of semitransparent colors)
|
||||||
|
*/
|
||||||
|
ALWAYS_INLINE static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px);
|
||||||
|
inline static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px)
|
||||||
|
{
|
||||||
|
float a = px.a/255.f;
|
||||||
|
|
||||||
|
return (f_pixel) {
|
||||||
|
.a = a,
|
||||||
|
.r = gamma_lut[px.r]*a,
|
||||||
|
.g = gamma_lut[px.g]*a,
|
||||||
|
.b = gamma_lut[px.b]*a,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static rgba_pixel f_to_rgb(const float gamma, const f_pixel px)
|
||||||
|
{
|
||||||
|
if (px.a < 1.f/256.f) {
|
||||||
|
return (rgba_pixel){0,0,0,0};
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = px.r / px.a,
|
||||||
|
g = px.g / px.a,
|
||||||
|
b = px.b / px.a,
|
||||||
|
a = px.a;
|
||||||
|
|
||||||
|
r = powf(r, gamma/internal_gamma);
|
||||||
|
g = powf(g, gamma/internal_gamma);
|
||||||
|
b = powf(b, gamma/internal_gamma);
|
||||||
|
|
||||||
|
// 256, because numbers are in range 1..255.9999… rounded down
|
||||||
|
r *= 256.f;
|
||||||
|
g *= 256.f;
|
||||||
|
b *= 256.f;
|
||||||
|
a *= 256.f;
|
||||||
|
|
||||||
|
return (rgba_pixel){
|
||||||
|
.r = r>=255.f ? 255 : r,
|
||||||
|
.g = g>=255.f ? 255 : g,
|
||||||
|
.b = b>=255.f ? 255 : b,
|
||||||
|
.a = a>=255.f ? 255 : a,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static double colordifference_ch(const double x, const double y, const double alphas);
|
||||||
|
inline static double colordifference_ch(const double x, const double y, const double alphas)
|
||||||
|
{
|
||||||
|
// maximum of channel blended on white, and blended on black
|
||||||
|
// premultiplied alpha and backgrounds 0/1 shorten the formula
|
||||||
|
const double black = x-y, white = black+alphas;
|
||||||
|
return MAX(black*black, white*white);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static float colordifference_stdc(const f_pixel px, const f_pixel py);
|
||||||
|
inline static float colordifference_stdc(const f_pixel px, const f_pixel py)
|
||||||
|
{
|
||||||
|
// px_b.rgb = px.rgb + 0*(1-px.a) // blend px on black
|
||||||
|
// px_b.a = px.a + 1*(1-px.a)
|
||||||
|
// px_w.rgb = px.rgb + 1*(1-px.a) // blend px on white
|
||||||
|
// px_w.a = px.a + 1*(1-px.a)
|
||||||
|
|
||||||
|
// px_b.rgb = px.rgb // difference same as in opaque RGB
|
||||||
|
// px_b.a = 1
|
||||||
|
// px_w.rgb = px.rgb - px.a // difference simplifies to formula below
|
||||||
|
// px_w.a = 1
|
||||||
|
|
||||||
|
// (px.rgb - px.a) - (py.rgb - py.a)
|
||||||
|
// (px.rgb - py.rgb) + (py.a - px.a)
|
||||||
|
|
||||||
|
const double alphas = py.a-px.a;
|
||||||
|
return colordifference_ch(px.r, py.r, alphas) +
|
||||||
|
colordifference_ch(px.g, py.g, alphas) +
|
||||||
|
colordifference_ch(px.b, py.b, alphas);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE static float colordifference(f_pixel px, f_pixel py);
|
||||||
|
inline static float colordifference(f_pixel px, f_pixel py)
|
||||||
|
{
|
||||||
|
#if USE_SSE
|
||||||
|
const __m128 vpx = _mm_load_ps((const float*)&px);
|
||||||
|
const __m128 vpy = _mm_load_ps((const float*)&py);
|
||||||
|
|
||||||
|
// y.a - x.a
|
||||||
|
__m128 alphas = _mm_sub_ss(vpy, vpx);
|
||||||
|
alphas = _mm_shuffle_ps(alphas,alphas,0); // copy first to all four
|
||||||
|
|
||||||
|
__m128 onblack = _mm_sub_ps(vpx, vpy); // x - y
|
||||||
|
__m128 onwhite = _mm_add_ps(onblack, alphas); // x - y + (y.a - x.a)
|
||||||
|
|
||||||
|
onblack = _mm_mul_ps(onblack, onblack);
|
||||||
|
onwhite = _mm_mul_ps(onwhite, onwhite);
|
||||||
|
const __m128 max = _mm_max_ps(onwhite, onblack);
|
||||||
|
|
||||||
|
// add rgb, not a
|
||||||
|
const __m128 maxhl = _mm_movehl_ps(max, max);
|
||||||
|
const __m128 tmp = _mm_add_ps(max, maxhl);
|
||||||
|
const __m128 sum = _mm_add_ss(maxhl, _mm_shuffle_ps(tmp, tmp, 1));
|
||||||
|
|
||||||
|
const float res = _mm_cvtss_f32(sum);
|
||||||
|
assert(fabs(res - colordifference_stdc(px,py)) < 0.001);
|
||||||
|
return res;
|
||||||
|
#else
|
||||||
|
return colordifference_stdc(px,py);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* from pamcmap.h */
|
||||||
|
union rgba_as_int {
|
||||||
|
rgba_pixel rgba;
|
||||||
|
unsigned int l;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
f_pixel acolor;
|
||||||
|
float adjusted_weight, // perceptual weight changed to tweak how mediancut selects colors
|
||||||
|
perceptual_weight; // number of pixels weighted by importance of different areas of the picture
|
||||||
|
|
||||||
|
float color_weight; // these two change every time histogram subset is sorted
|
||||||
|
union {
|
||||||
|
unsigned int sort_value;
|
||||||
|
unsigned char likely_colormap_index;
|
||||||
|
} tmp;
|
||||||
|
} hist_item;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hist_item *achv;
|
||||||
|
void (*free)(void*);
|
||||||
|
double total_perceptual_weight;
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int ignorebits;
|
||||||
|
} histogram;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
f_pixel acolor;
|
||||||
|
float popularity;
|
||||||
|
bool fixed; // if true it's user-supplied and must not be changed (e.g in K-Means iteration)
|
||||||
|
} colormap_item;
|
||||||
|
|
||||||
|
typedef struct colormap {
|
||||||
|
unsigned int colors;
|
||||||
|
void* (*malloc)(size_t);
|
||||||
|
void (*free)(void*);
|
||||||
|
colormap_item palette[];
|
||||||
|
} colormap;
|
||||||
|
|
||||||
|
struct acolorhist_arr_item {
|
||||||
|
union rgba_as_int color;
|
||||||
|
unsigned int perceptual_weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct acolorhist_arr_head {
|
||||||
|
struct acolorhist_arr_item inline1, inline2;
|
||||||
|
unsigned int used, capacity;
|
||||||
|
struct acolorhist_arr_item *other_items;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct acolorhash_table {
|
||||||
|
struct mempool *mempool;
|
||||||
|
unsigned int ignorebits, maxcolors, colors, cols, rows;
|
||||||
|
unsigned int hash_size;
|
||||||
|
unsigned int freestackp;
|
||||||
|
struct acolorhist_arr_item *freestack[512];
|
||||||
|
struct acolorhist_arr_head buckets[];
|
||||||
|
};
|
||||||
|
|
||||||
|
LIQ_PRIVATE void pam_freeacolorhash(struct acolorhash_table *acht);
|
||||||
|
LIQ_PRIVATE struct acolorhash_table *pam_allocacolorhash(unsigned int maxcolors, unsigned int surface, unsigned int ignorebits, void* (*malloc)(size_t), void (*free)(void*));
|
||||||
|
LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table *acht, const double gamma, void* (*malloc)(size_t), void (*free)(void*));
|
||||||
|
LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba_pixel *const pixels[], unsigned int cols, unsigned int rows, const unsigned char *importance_map);
|
||||||
|
LIQ_PRIVATE bool pam_add_to_hash(struct acolorhash_table *acht, unsigned int hash, unsigned int boost, union rgba_as_int px, unsigned int row, unsigned int rows);
|
||||||
|
|
||||||
|
LIQ_PRIVATE void pam_freeacolorhist(histogram *h);
|
||||||
|
|
||||||
|
LIQ_PRIVATE colormap *pam_colormap(unsigned int colors, void* (*malloc)(size_t), void (*free)(void*));
|
||||||
|
LIQ_PRIVATE colormap *pam_duplicate_colormap(colormap *map);
|
||||||
|
LIQ_PRIVATE void pam_freecolormap(colormap *c);
|
||||||
|
|
||||||
|
#endif
|
||||||
21
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
21
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
@@ -30,9 +30,9 @@ type TwoQueueCache struct {
|
|||||||
size int
|
size int
|
||||||
recentSize int
|
recentSize int
|
||||||
|
|
||||||
recent *simplelru.LRU
|
recent simplelru.LRUCache
|
||||||
frequent *simplelru.LRU
|
frequent simplelru.LRUCache
|
||||||
recentEvict *simplelru.LRU
|
recentEvict simplelru.LRUCache
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,8 @@ func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCa
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
// Get looks up a key's value from the cache.
|
||||||
|
func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
@@ -105,6 +106,7 @@ func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds a value to the cache.
|
||||||
func (c *TwoQueueCache) Add(key, value interface{}) {
|
func (c *TwoQueueCache) Add(key, value interface{}) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@@ -160,12 +162,15 @@ func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
|
|||||||
c.frequent.RemoveOldest()
|
c.frequent.RemoveOldest()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the cache.
|
||||||
func (c *TwoQueueCache) Len() int {
|
func (c *TwoQueueCache) Len() int {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
return c.recent.Len() + c.frequent.Len()
|
return c.recent.Len() + c.frequent.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of the keys in the cache.
|
||||||
|
// The frequently used keys are first in the returned slice.
|
||||||
func (c *TwoQueueCache) Keys() []interface{} {
|
func (c *TwoQueueCache) Keys() []interface{} {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
@@ -174,6 +179,7 @@ func (c *TwoQueueCache) Keys() []interface{} {
|
|||||||
return append(k1, k2...)
|
return append(k1, k2...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided key from the cache.
|
||||||
func (c *TwoQueueCache) Remove(key interface{}) {
|
func (c *TwoQueueCache) Remove(key interface{}) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@@ -188,6 +194,7 @@ func (c *TwoQueueCache) Remove(key interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge is used to completely clear the cache.
|
||||||
func (c *TwoQueueCache) Purge() {
|
func (c *TwoQueueCache) Purge() {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@@ -196,13 +203,17 @@ func (c *TwoQueueCache) Purge() {
|
|||||||
c.recentEvict.Purge()
|
c.recentEvict.Purge()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Contains is used to check if the cache contains a key
|
||||||
|
// without updating recency or frequency.
|
||||||
func (c *TwoQueueCache) Contains(key interface{}) bool {
|
func (c *TwoQueueCache) Contains(key interface{}) bool {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
return c.frequent.Contains(key) || c.recent.Contains(key)
|
return c.frequent.Contains(key) || c.recent.Contains(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
|
// Peek is used to inspect the cache value of a key
|
||||||
|
// without updating recency or frequency.
|
||||||
|
func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
if val, ok := c.frequent.Peek(key); ok {
|
if val, ok := c.frequent.Peek(key); ok {
|
||||||
|
|||||||
306
vendor/github.com/hashicorp/golang-lru/2q_test.go
generated
vendored
306
vendor/github.com/hashicorp/golang-lru/2q_test.go
generated
vendored
@@ -1,306 +0,0 @@
|
|||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Benchmark2Q_Rand(b *testing.B) {
|
|
||||||
l, err := New2Q(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < 2*b.N; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
} else {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark2Q_Freq(b *testing.B) {
|
|
||||||
l, err := New2Q(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
trace[i] = rand.Int63() % 16384
|
|
||||||
} else {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
}
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2Q_RandomOps(t *testing.T) {
|
|
||||||
size := 128
|
|
||||||
l, err := New2Q(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := 200000
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
key := rand.Int63() % 512
|
|
||||||
r := rand.Int63()
|
|
||||||
switch r % 3 {
|
|
||||||
case 0:
|
|
||||||
l.Add(key, key)
|
|
||||||
case 1:
|
|
||||||
l.Get(key)
|
|
||||||
case 2:
|
|
||||||
l.Remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.recent.Len()+l.frequent.Len() > size {
|
|
||||||
t.Fatalf("bad: recent: %d freq: %d",
|
|
||||||
l.recent.Len(), l.frequent.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2Q_Get_RecentToFrequent(t *testing.T) {
|
|
||||||
l, err := New2Q(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touch all the entries, should be in t1
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if n := l.recent.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get should upgrade to t2
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n := l.recent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get be from t2
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n := l.recent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2Q_Add_RecentToFrequent(t *testing.T) {
|
|
||||||
l, err := New2Q(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add initially to recent
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.recent.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add should upgrade to frequent
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.recent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add should remain in frequent
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.recent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2Q_Add_RecentEvict(t *testing.T) {
|
|
||||||
l, err := New2Q(4)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 1,2,3,4,5 -> Evict 1
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
l.Add(3, 3)
|
|
||||||
l.Add(4, 4)
|
|
||||||
l.Add(5, 5)
|
|
||||||
if n := l.recent.Len(); n != 4 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.recentEvict.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull in the recently evicted
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.recent.Len(); n != 3 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.recentEvict.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 6, should cause another recent evict
|
|
||||||
l.Add(6, 6)
|
|
||||||
if n := l.recent.Len(); n != 3 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.recentEvict.Len(); n != 2 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.frequent.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2Q(t *testing.T) {
|
|
||||||
l, err := New2Q(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if l.Len() != 128 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
|
|
||||||
t.Fatalf("bad key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 256; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("should not be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 192; i++ {
|
|
||||||
l.Remove(i)
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Purge()
|
|
||||||
if l.Len() != 0 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
if _, ok := l.Get(200); ok {
|
|
||||||
t.Fatalf("should contain nothing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Contains doesn't update recent-ness
|
|
||||||
func Test2Q_Contains(t *testing.T) {
|
|
||||||
l, err := New2Q(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if !l.Contains(1) {
|
|
||||||
t.Errorf("1 should be contained")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("Contains should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Peek doesn't update recent-ness
|
|
||||||
func Test2Q_Peek(t *testing.T) {
|
|
||||||
l, err := New2Q(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if v, ok := l.Peek(1); !ok || v != 1 {
|
|
||||||
t.Errorf("1 should be set to 1: %v, %v", v, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
16
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
@@ -18,11 +18,11 @@ type ARCCache struct {
|
|||||||
size int // Size is the total capacity of the cache
|
size int // Size is the total capacity of the cache
|
||||||
p int // P is the dynamic preference towards T1 or T2
|
p int // P is the dynamic preference towards T1 or T2
|
||||||
|
|
||||||
t1 *simplelru.LRU // T1 is the LRU for recently accessed items
|
t1 simplelru.LRUCache // T1 is the LRU for recently accessed items
|
||||||
b1 *simplelru.LRU // B1 is the LRU for evictions from t1
|
b1 simplelru.LRUCache // B1 is the LRU for evictions from t1
|
||||||
|
|
||||||
t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
|
t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items
|
||||||
b2 *simplelru.LRU // B2 is the LRU for evictions from t2
|
b2 simplelru.LRUCache // B2 is the LRU for evictions from t2
|
||||||
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -60,11 +60,11 @@ func NewARC(size int) (*ARCCache, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get looks up a key's value from the cache.
|
// Get looks up a key's value from the cache.
|
||||||
func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
|
func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
// Ff the value is contained in T1 (recent), then
|
// If the value is contained in T1 (recent), then
|
||||||
// promote it to T2 (frequent)
|
// promote it to T2 (frequent)
|
||||||
if val, ok := c.t1.Peek(key); ok {
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
c.t1.Remove(key)
|
c.t1.Remove(key)
|
||||||
@@ -153,7 +153,7 @@ func (c *ARCCache) Add(key, value interface{}) {
|
|||||||
// Remove from B2
|
// Remove from B2
|
||||||
c.b2.Remove(key)
|
c.b2.Remove(key)
|
||||||
|
|
||||||
// Add the key to the frequntly used list
|
// Add the key to the frequently used list
|
||||||
c.t2.Add(key, value)
|
c.t2.Add(key, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ func (c *ARCCache) Contains(key interface{}) bool {
|
|||||||
|
|
||||||
// Peek is used to inspect the cache value of a key
|
// Peek is used to inspect the cache value of a key
|
||||||
// without updating recency or frequency.
|
// without updating recency or frequency.
|
||||||
func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
|
func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
if val, ok := c.t1.Peek(key); ok {
|
if val, ok := c.t1.Peek(key); ok {
|
||||||
|
|||||||
377
vendor/github.com/hashicorp/golang-lru/arc_test.go
generated
vendored
377
vendor/github.com/hashicorp/golang-lru/arc_test.go
generated
vendored
@@ -1,377 +0,0 @@
|
|||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkARC_Rand(b *testing.B) {
|
|
||||||
l, err := NewARC(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < 2*b.N; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
} else {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkARC_Freq(b *testing.B) {
|
|
||||||
l, err := NewARC(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
trace[i] = rand.Int63() % 16384
|
|
||||||
} else {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
}
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestARC_RandomOps(t *testing.T) {
|
|
||||||
size := 128
|
|
||||||
l, err := NewARC(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := 200000
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
key := rand.Int63() % 512
|
|
||||||
r := rand.Int63()
|
|
||||||
switch r % 3 {
|
|
||||||
case 0:
|
|
||||||
l.Add(key, key)
|
|
||||||
case 1:
|
|
||||||
l.Get(key)
|
|
||||||
case 2:
|
|
||||||
l.Remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.t1.Len()+l.t2.Len() > size {
|
|
||||||
t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
|
|
||||||
l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
|
|
||||||
}
|
|
||||||
if l.b1.Len()+l.b2.Len() > size {
|
|
||||||
t.Fatalf("bad: t1: %d t2: %d b1: %d b2: %d p: %d",
|
|
||||||
l.t1.Len(), l.t2.Len(), l.b1.Len(), l.b2.Len(), l.p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestARC_Get_RecentToFrequent(t *testing.T) {
|
|
||||||
l, err := NewARC(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touch all the entries, should be in t1
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if n := l.t1.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get should upgrade to t2
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get be from t2
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 128 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestARC_Add_RecentToFrequent(t *testing.T) {
|
|
||||||
l, err := NewARC(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add initially to t1
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.t1.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add should upgrade to t2
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add should remain in t2
|
|
||||||
l.Add(1, 1)
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestARC_Adaptive(t *testing.T) {
|
|
||||||
l, err := NewARC(4)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill t1
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if n := l.t1.Len(); n != 4 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to t2
|
|
||||||
l.Get(0)
|
|
||||||
l.Get(1)
|
|
||||||
if n := l.t2.Len(); n != 2 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evict from t1
|
|
||||||
l.Add(4, 4)
|
|
||||||
if n := l.b1.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current state
|
|
||||||
// t1 : (MRU) [4, 3] (LRU)
|
|
||||||
// t2 : (MRU) [1, 0] (LRU)
|
|
||||||
// b1 : (MRU) [2] (LRU)
|
|
||||||
// b2 : (MRU) [] (LRU)
|
|
||||||
|
|
||||||
// Add 2, should cause hit on b1
|
|
||||||
l.Add(2, 2)
|
|
||||||
if n := l.b1.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if l.p != 1 {
|
|
||||||
t.Fatalf("bad: %d", l.p)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 3 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current state
|
|
||||||
// t1 : (MRU) [4] (LRU)
|
|
||||||
// t2 : (MRU) [2, 1, 0] (LRU)
|
|
||||||
// b1 : (MRU) [3] (LRU)
|
|
||||||
// b2 : (MRU) [] (LRU)
|
|
||||||
|
|
||||||
// Add 4, should migrate to t2
|
|
||||||
l.Add(4, 4)
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 4 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current state
|
|
||||||
// t1 : (MRU) [] (LRU)
|
|
||||||
// t2 : (MRU) [4, 2, 1, 0] (LRU)
|
|
||||||
// b1 : (MRU) [3] (LRU)
|
|
||||||
// b2 : (MRU) [] (LRU)
|
|
||||||
|
|
||||||
// Add 4, should evict to b2
|
|
||||||
l.Add(5, 5)
|
|
||||||
if n := l.t1.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 3 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.b2.Len(); n != 1 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current state
|
|
||||||
// t1 : (MRU) [5] (LRU)
|
|
||||||
// t2 : (MRU) [4, 2, 1] (LRU)
|
|
||||||
// b1 : (MRU) [3] (LRU)
|
|
||||||
// b2 : (MRU) [0] (LRU)
|
|
||||||
|
|
||||||
// Add 0, should decrease p
|
|
||||||
l.Add(0, 0)
|
|
||||||
if n := l.t1.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.t2.Len(); n != 4 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.b1.Len(); n != 2 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if n := l.b2.Len(); n != 0 {
|
|
||||||
t.Fatalf("bad: %d", n)
|
|
||||||
}
|
|
||||||
if l.p != 0 {
|
|
||||||
t.Fatalf("bad: %d", l.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current state
|
|
||||||
// t1 : (MRU) [] (LRU)
|
|
||||||
// t2 : (MRU) [0, 4, 2, 1] (LRU)
|
|
||||||
// b1 : (MRU) [5, 3] (LRU)
|
|
||||||
// b2 : (MRU) [0] (LRU)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestARC(t *testing.T) {
|
|
||||||
l, err := NewARC(128)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if l.Len() != 128 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
|
|
||||||
t.Fatalf("bad key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 256; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("should not be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 192; i++ {
|
|
||||||
l.Remove(i)
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Purge()
|
|
||||||
if l.Len() != 0 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
if _, ok := l.Get(200); ok {
|
|
||||||
t.Fatalf("should contain nothing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Contains doesn't update recent-ness
|
|
||||||
func TestARC_Contains(t *testing.T) {
|
|
||||||
l, err := NewARC(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if !l.Contains(1) {
|
|
||||||
t.Errorf("1 should be contained")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("Contains should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Peek doesn't update recent-ness
|
|
||||||
func TestARC_Peek(t *testing.T) {
|
|
||||||
l, err := NewARC(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if v, ok := l.Peek(1); !ok || v != 1 {
|
|
||||||
t.Errorf("1 should be set to 1: %v, %v", v, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
vendor/github.com/hashicorp/golang-lru/doc.go
generated
vendored
Normal file
21
vendor/github.com/hashicorp/golang-lru/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Package lru provides three different LRU caches of varying sophistication.
|
||||||
|
//
|
||||||
|
// Cache is a simple LRU cache. It is based on the
|
||||||
|
// LRU implementation in groupcache:
|
||||||
|
// https://github.com/golang/groupcache/tree/master/lru
|
||||||
|
//
|
||||||
|
// TwoQueueCache tracks frequently used and recently used entries separately.
|
||||||
|
// This avoids a burst of accesses from taking out frequently used entries,
|
||||||
|
// at the cost of about 2x computational overhead and some extra bookkeeping.
|
||||||
|
//
|
||||||
|
// ARCCache is an adaptive replacement cache. It tracks recent evictions as
|
||||||
|
// well as recent usage in both the frequent and recent caches. Its
|
||||||
|
// computational overhead is comparable to TwoQueueCache, but the memory
|
||||||
|
// overhead is linear with the size of the cache.
|
||||||
|
//
|
||||||
|
// ARC has been patented by IBM, so do not use it if that is problematic for
|
||||||
|
// your program.
|
||||||
|
//
|
||||||
|
// All caches in this package take locks while operating, and are therefore
|
||||||
|
// thread-safe for consumers.
|
||||||
|
package lru
|
||||||
1
vendor/github.com/hashicorp/golang-lru/go.mod
generated
vendored
Normal file
1
vendor/github.com/hashicorp/golang-lru/go.mod
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module github.com/hashicorp/golang-lru
|
||||||
28
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
28
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
@@ -1,6 +1,3 @@
|
|||||||
// This package provides a simple LRU cache. It is based on the
|
|
||||||
// LRU implementation in groupcache:
|
|
||||||
// https://github.com/golang/groupcache/tree/master/lru
|
|
||||||
package lru
|
package lru
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -11,11 +8,11 @@ import (
|
|||||||
|
|
||||||
// Cache is a thread-safe fixed size LRU cache.
|
// Cache is a thread-safe fixed size LRU cache.
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
lru *simplelru.LRU
|
lru simplelru.LRUCache
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates an LRU of the given size
|
// New creates an LRU of the given size.
|
||||||
func New(size int) (*Cache, error) {
|
func New(size int) (*Cache, error) {
|
||||||
return NewWithEvict(size, nil)
|
return NewWithEvict(size, nil)
|
||||||
}
|
}
|
||||||
@@ -33,7 +30,7 @@ func NewWithEvict(size int, onEvicted func(key interface{}, value interface{}))
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge is used to completely clear the cache
|
// Purge is used to completely clear the cache.
|
||||||
func (c *Cache) Purge() {
|
func (c *Cache) Purge() {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
c.lru.Purge()
|
c.lru.Purge()
|
||||||
@@ -41,30 +38,30 @@ func (c *Cache) Purge() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
func (c *Cache) Add(key, value interface{}) bool {
|
func (c *Cache) Add(key, value interface{}) (evicted bool) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
return c.lru.Add(key, value)
|
return c.lru.Add(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get looks up a key's value from the cache.
|
// Get looks up a key's value from the cache.
|
||||||
func (c *Cache) Get(key interface{}) (interface{}, bool) {
|
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
return c.lru.Get(key)
|
return c.lru.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a key is in the cache, without updating the recent-ness
|
// Contains checks if a key is in the cache, without updating the
|
||||||
// or deleting it for being stale.
|
// recent-ness or deleting it for being stale.
|
||||||
func (c *Cache) Contains(key interface{}) bool {
|
func (c *Cache) Contains(key interface{}) bool {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
return c.lru.Contains(key)
|
return c.lru.Contains(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the key value (or undefined if not found) without updating
|
// Peek returns the key value (or undefined if not found) without updating
|
||||||
// the "recently used"-ness of the key.
|
// the "recently used"-ness of the key.
|
||||||
func (c *Cache) Peek(key interface{}) (interface{}, bool) {
|
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||||
c.lock.RLock()
|
c.lock.RLock()
|
||||||
defer c.lock.RUnlock()
|
defer c.lock.RUnlock()
|
||||||
return c.lru.Peek(key)
|
return c.lru.Peek(key)
|
||||||
@@ -73,16 +70,15 @@ func (c *Cache) Peek(key interface{}) (interface{}, bool) {
|
|||||||
// ContainsOrAdd checks if a key is in the cache without updating the
|
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||||
// Returns whether found and whether an eviction occurred.
|
// Returns whether found and whether an eviction occurred.
|
||||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
|
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
if c.lru.Contains(key) {
|
if c.lru.Contains(key) {
|
||||||
return true, false
|
return true, false
|
||||||
} else {
|
|
||||||
evict := c.lru.Add(key, value)
|
|
||||||
return false, evict
|
|
||||||
}
|
}
|
||||||
|
evicted = c.lru.Add(key, value)
|
||||||
|
return false, evicted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the provided key from the cache.
|
// Remove removes the provided key from the cache.
|
||||||
|
|||||||
221
vendor/github.com/hashicorp/golang-lru/lru_test.go
generated
vendored
221
vendor/github.com/hashicorp/golang-lru/lru_test.go
generated
vendored
@@ -1,221 +0,0 @@
|
|||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkLRU_Rand(b *testing.B) {
|
|
||||||
l, err := New(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < 2*b.N; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
} else {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLRU_Freq(b *testing.B) {
|
|
||||||
l, err := New(8192)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]int64, b.N*2)
|
|
||||||
for i := 0; i < b.N*2; i++ {
|
|
||||||
if i%2 == 0 {
|
|
||||||
trace[i] = rand.Int63() % 16384
|
|
||||||
} else {
|
|
||||||
trace[i] = rand.Int63() % 32768
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
l.Add(trace[i], trace[i])
|
|
||||||
}
|
|
||||||
var hit, miss int
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, ok := l.Get(trace[i])
|
|
||||||
if ok {
|
|
||||||
hit++
|
|
||||||
} else {
|
|
||||||
miss++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(miss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLRU(t *testing.T) {
|
|
||||||
evictCounter := 0
|
|
||||||
onEvicted := func(k interface{}, v interface{}) {
|
|
||||||
if k != v {
|
|
||||||
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
|
|
||||||
}
|
|
||||||
evictCounter += 1
|
|
||||||
}
|
|
||||||
l, err := NewWithEvict(128, onEvicted)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if l.Len() != 128 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
if evictCounter != 128 {
|
|
||||||
t.Fatalf("bad evict count: %v", evictCounter)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
|
|
||||||
t.Fatalf("bad key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 256; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("should not be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 192; i++ {
|
|
||||||
l.Remove(i)
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Get(192) // expect 192 to be last key in l.Keys()
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
|
|
||||||
t.Fatalf("out of order key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Purge()
|
|
||||||
if l.Len() != 0 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
if _, ok := l.Get(200); ok {
|
|
||||||
t.Fatalf("should contain nothing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that Add returns true/false if an eviction occurred
|
|
||||||
func TestLRUAdd(t *testing.T) {
|
|
||||||
evictCounter := 0
|
|
||||||
onEvicted := func(k interface{}, v interface{}) {
|
|
||||||
evictCounter += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
l, err := NewWithEvict(1, onEvicted)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Add(1, 1) == true || evictCounter != 0 {
|
|
||||||
t.Errorf("should not have an eviction")
|
|
||||||
}
|
|
||||||
if l.Add(2, 2) == false || evictCounter != 1 {
|
|
||||||
t.Errorf("should have an eviction")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that Contains doesn't update recent-ness
|
|
||||||
func TestLRUContains(t *testing.T) {
|
|
||||||
l, err := New(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if !l.Contains(1) {
|
|
||||||
t.Errorf("1 should be contained")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("Contains should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that Contains doesn't update recent-ness
|
|
||||||
func TestLRUContainsOrAdd(t *testing.T) {
|
|
||||||
l, err := New(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
contains, evict := l.ContainsOrAdd(1, 1)
|
|
||||||
if !contains {
|
|
||||||
t.Errorf("1 should be contained")
|
|
||||||
}
|
|
||||||
if evict {
|
|
||||||
t.Errorf("nothing should be evicted here")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
contains, evict = l.ContainsOrAdd(1, 1)
|
|
||||||
if contains {
|
|
||||||
t.Errorf("1 should not have been contained")
|
|
||||||
}
|
|
||||||
if !evict {
|
|
||||||
t.Errorf("an eviction should have occurred")
|
|
||||||
}
|
|
||||||
if !l.Contains(1) {
|
|
||||||
t.Errorf("now 1 should be contained")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that Peek doesn't update recent-ness
|
|
||||||
func TestLRUPeek(t *testing.T) {
|
|
||||||
l, err := New(2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if v, ok := l.Peek(1); !ok || v != 1 {
|
|
||||||
t.Errorf("1 should be set to 1: %v, %v", v, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
17
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
@@ -36,7 +36,7 @@ func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge is used to completely clear the cache
|
// Purge is used to completely clear the cache.
|
||||||
func (c *LRU) Purge() {
|
func (c *LRU) Purge() {
|
||||||
for k, v := range c.items {
|
for k, v := range c.items {
|
||||||
if c.onEvict != nil {
|
if c.onEvict != nil {
|
||||||
@@ -48,7 +48,7 @@ func (c *LRU) Purge() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||||
func (c *LRU) Add(key, value interface{}) bool {
|
func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
||||||
// Check for existing item
|
// Check for existing item
|
||||||
if ent, ok := c.items[key]; ok {
|
if ent, ok := c.items[key]; ok {
|
||||||
c.evictList.MoveToFront(ent)
|
c.evictList.MoveToFront(ent)
|
||||||
@@ -78,17 +78,18 @@ func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a key is in the cache, without updating the recent-ness
|
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||||
// or deleting it for being stale.
|
// or deleting it for being stale.
|
||||||
func (c *LRU) Contains(key interface{}) (ok bool) {
|
func (c *LRU) Contains(key interface{}) (ok bool) {
|
||||||
_, ok = c.items[key]
|
_, ok = c.items[key]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the key value (or undefined if not found) without updating
|
// Peek returns the key value (or undefined if not found) without updating
|
||||||
// the "recently used"-ness of the key.
|
// the "recently used"-ness of the key.
|
||||||
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||||
if ent, ok := c.items[key]; ok {
|
var ent *list.Element
|
||||||
|
if ent, ok = c.items[key]; ok {
|
||||||
return ent.Value.(*entry).value, true
|
return ent.Value.(*entry).value, true
|
||||||
}
|
}
|
||||||
return nil, ok
|
return nil, ok
|
||||||
@@ -96,7 +97,7 @@ func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
|||||||
|
|
||||||
// Remove removes the provided key from the cache, returning if the
|
// Remove removes the provided key from the cache, returning if the
|
||||||
// key was contained.
|
// key was contained.
|
||||||
func (c *LRU) Remove(key interface{}) bool {
|
func (c *LRU) Remove(key interface{}) (present bool) {
|
||||||
if ent, ok := c.items[key]; ok {
|
if ent, ok := c.items[key]; ok {
|
||||||
c.removeElement(ent)
|
c.removeElement(ent)
|
||||||
return true
|
return true
|
||||||
@@ -105,7 +106,7 @@ func (c *LRU) Remove(key interface{}) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOldest removes the oldest item from the cache.
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
|
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||||
ent := c.evictList.Back()
|
ent := c.evictList.Back()
|
||||||
if ent != nil {
|
if ent != nil {
|
||||||
c.removeElement(ent)
|
c.removeElement(ent)
|
||||||
@@ -116,7 +117,7 @@ func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetOldest returns the oldest entry
|
// GetOldest returns the oldest entry
|
||||||
func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
|
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||||
ent := c.evictList.Back()
|
ent := c.evictList.Back()
|
||||||
if ent != nil {
|
if ent != nil {
|
||||||
kv := ent.Value.(*entry)
|
kv := ent.Value.(*entry)
|
||||||
|
|||||||
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package simplelru
|
||||||
|
|
||||||
|
// LRUCache is the interface for simple LRU cache.
|
||||||
|
type LRUCache interface {
|
||||||
|
// Adds a value to the cache, returns true if an eviction occurred and
|
||||||
|
// updates the "recently used"-ness of the key.
|
||||||
|
Add(key, value interface{}) bool
|
||||||
|
|
||||||
|
// Returns key's value from the cache and
|
||||||
|
// updates the "recently used"-ness of the key. #value, isFound
|
||||||
|
Get(key interface{}) (value interface{}, ok bool)
|
||||||
|
|
||||||
|
// Check if a key exsists in cache without updating the recent-ness.
|
||||||
|
Contains(key interface{}) (ok bool)
|
||||||
|
|
||||||
|
// Returns key's value without updating the "recently used"-ness of the key.
|
||||||
|
Peek(key interface{}) (value interface{}, ok bool)
|
||||||
|
|
||||||
|
// Removes a key from the cache.
|
||||||
|
Remove(key interface{}) bool
|
||||||
|
|
||||||
|
// Removes the oldest entry from cache.
|
||||||
|
RemoveOldest() (interface{}, interface{}, bool)
|
||||||
|
|
||||||
|
// Returns the oldest entry from the cache. #key, value, isFound
|
||||||
|
GetOldest() (interface{}, interface{}, bool)
|
||||||
|
|
||||||
|
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||||
|
Keys() []interface{}
|
||||||
|
|
||||||
|
// Returns the number of items in the cache.
|
||||||
|
Len() int
|
||||||
|
|
||||||
|
// Clear all cache entries
|
||||||
|
Purge()
|
||||||
|
}
|
||||||
167
vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
generated
vendored
167
vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
generated
vendored
@@ -1,167 +0,0 @@
|
|||||||
package simplelru
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestLRU(t *testing.T) {
|
|
||||||
evictCounter := 0
|
|
||||||
onEvicted := func(k interface{}, v interface{}) {
|
|
||||||
if k != v {
|
|
||||||
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
|
|
||||||
}
|
|
||||||
evictCounter += 1
|
|
||||||
}
|
|
||||||
l, err := NewLRU(128, onEvicted)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
if l.Len() != 128 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
if evictCounter != 128 {
|
|
||||||
t.Fatalf("bad evict count: %v", evictCounter)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if v, ok := l.Get(k); !ok || v != k || v != i+128 {
|
|
||||||
t.Fatalf("bad key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 256; i++ {
|
|
||||||
_, ok := l.Get(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("should not be evicted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 128; i < 192; i++ {
|
|
||||||
ok := l.Remove(i)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("should be contained")
|
|
||||||
}
|
|
||||||
ok = l.Remove(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should not be contained")
|
|
||||||
}
|
|
||||||
_, ok = l.Get(i)
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("should be deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Get(192) // expect 192 to be last key in l.Keys()
|
|
||||||
|
|
||||||
for i, k := range l.Keys() {
|
|
||||||
if (i < 63 && k != i+193) || (i == 63 && k != 192) {
|
|
||||||
t.Fatalf("out of order key: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Purge()
|
|
||||||
if l.Len() != 0 {
|
|
||||||
t.Fatalf("bad len: %v", l.Len())
|
|
||||||
}
|
|
||||||
if _, ok := l.Get(200); ok {
|
|
||||||
t.Fatalf("should contain nothing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
|
|
||||||
l, err := NewLRU(128, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
l.Add(i, i)
|
|
||||||
}
|
|
||||||
k, _, ok := l.GetOldest()
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing")
|
|
||||||
}
|
|
||||||
if k.(int) != 128 {
|
|
||||||
t.Fatalf("bad: %v", k)
|
|
||||||
}
|
|
||||||
|
|
||||||
k, _, ok = l.RemoveOldest()
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing")
|
|
||||||
}
|
|
||||||
if k.(int) != 128 {
|
|
||||||
t.Fatalf("bad: %v", k)
|
|
||||||
}
|
|
||||||
|
|
||||||
k, _, ok = l.RemoveOldest()
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("missing")
|
|
||||||
}
|
|
||||||
if k.(int) != 129 {
|
|
||||||
t.Fatalf("bad: %v", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Add returns true/false if an eviction occurred
|
|
||||||
func TestLRU_Add(t *testing.T) {
|
|
||||||
evictCounter := 0
|
|
||||||
onEvicted := func(k interface{}, v interface{}) {
|
|
||||||
evictCounter += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
l, err := NewLRU(1, onEvicted)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Add(1, 1) == true || evictCounter != 0 {
|
|
||||||
t.Errorf("should not have an eviction")
|
|
||||||
}
|
|
||||||
if l.Add(2, 2) == false || evictCounter != 1 {
|
|
||||||
t.Errorf("should have an eviction")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Contains doesn't update recent-ness
|
|
||||||
func TestLRU_Contains(t *testing.T) {
|
|
||||||
l, err := NewLRU(2, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if !l.Contains(1) {
|
|
||||||
t.Errorf("1 should be contained")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("Contains should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Peek doesn't update recent-ness
|
|
||||||
func TestLRU_Peek(t *testing.T) {
|
|
||||||
l, err := NewLRU(2, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(1, 1)
|
|
||||||
l.Add(2, 2)
|
|
||||||
if v, ok := l.Peek(1); !ok || v != 1 {
|
|
||||||
t.Errorf("1 should be set to 1: %v, %v", v, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Add(3, 3)
|
|
||||||
if l.Contains(1) {
|
|
||||||
t.Errorf("should not have updated recent-ness of 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
vendor/golang.org/x/image/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/image/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
||||||
3
vendor/golang.org/x/image/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/image/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||||
27
vendor/golang.org/x/image/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/image/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
22
vendor/golang.org/x/image/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/image/PATENTS
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
||||||
199
vendor/golang.org/x/image/bmp/reader.go
generated
vendored
Normal file
199
vendor/golang.org/x/image/bmp/reader.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package bmp implements a BMP image decoder and encoder.
|
||||||
|
//
|
||||||
|
// The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html.
|
||||||
|
package bmp // import "golang.org/x/image/bmp"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupported means that the input BMP image uses a valid but unsupported
|
||||||
|
// feature.
|
||||||
|
var ErrUnsupported = errors.New("bmp: unsupported BMP image")
|
||||||
|
|
||||||
|
func readUint16(b []byte) uint16 {
|
||||||
|
return uint16(b[0]) | uint16(b[1])<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUint32(b []byte) uint32 {
|
||||||
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePaletted reads an 8 bit-per-pixel BMP image from r.
|
||||||
|
// If topDown is false, the image rows will be read bottom-up.
|
||||||
|
func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||||
|
paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette))
|
||||||
|
if c.Width == 0 || c.Height == 0 {
|
||||||
|
return paletted, nil
|
||||||
|
}
|
||||||
|
var tmp [4]byte
|
||||||
|
y0, y1, yDelta := c.Height-1, -1, -1
|
||||||
|
if topDown {
|
||||||
|
y0, y1, yDelta = 0, c.Height, +1
|
||||||
|
}
|
||||||
|
for y := y0; y != y1; y += yDelta {
|
||||||
|
p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width]
|
||||||
|
if _, err := io.ReadFull(r, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Each row is 4-byte aligned.
|
||||||
|
if c.Width%4 != 0 {
|
||||||
|
_, err := io.ReadFull(r, tmp[:4-c.Width%4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paletted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeRGB reads a 24 bit-per-pixel BMP image from r.
|
||||||
|
// If topDown is false, the image rows will be read bottom-up.
|
||||||
|
func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||||
|
rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height))
|
||||||
|
if c.Width == 0 || c.Height == 0 {
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
// There are 3 bytes per pixel, and each row is 4-byte aligned.
|
||||||
|
b := make([]byte, (3*c.Width+3)&^3)
|
||||||
|
y0, y1, yDelta := c.Height-1, -1, -1
|
||||||
|
if topDown {
|
||||||
|
y0, y1, yDelta = 0, c.Height, +1
|
||||||
|
}
|
||||||
|
for y := y0; y != y1; y += yDelta {
|
||||||
|
if _, err := io.ReadFull(r, b); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
|
||||||
|
for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
|
||||||
|
// BMP images are stored in BGR order rather than RGB order.
|
||||||
|
p[i+0] = b[j+2]
|
||||||
|
p[i+1] = b[j+1]
|
||||||
|
p[i+2] = b[j+0]
|
||||||
|
p[i+3] = 0xFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeNRGBA reads a 32 bit-per-pixel BMP image from r.
|
||||||
|
// If topDown is false, the image rows will be read bottom-up.
|
||||||
|
func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||||
|
rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height))
|
||||||
|
if c.Width == 0 || c.Height == 0 {
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
y0, y1, yDelta := c.Height-1, -1, -1
|
||||||
|
if topDown {
|
||||||
|
y0, y1, yDelta = 0, c.Height, +1
|
||||||
|
}
|
||||||
|
for y := y0; y != y1; y += yDelta {
|
||||||
|
p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
|
||||||
|
if _, err := io.ReadFull(r, p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(p); i += 4 {
|
||||||
|
// BMP images are stored in BGRA order rather than RGBA order.
|
||||||
|
p[i+0], p[i+2] = p[i+2], p[i+0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rgba, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads a BMP image from r and returns it as an image.Image.
|
||||||
|
// Limitation: The file must be 8, 24 or 32 bits per pixel.
|
||||||
|
func Decode(r io.Reader) (image.Image, error) {
|
||||||
|
c, bpp, topDown, err := decodeConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch bpp {
|
||||||
|
case 8:
|
||||||
|
return decodePaletted(r, c, topDown)
|
||||||
|
case 24:
|
||||||
|
return decodeRGB(r, c, topDown)
|
||||||
|
case 32:
|
||||||
|
return decodeNRGBA(r, c, topDown)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeConfig returns the color model and dimensions of a BMP image without
|
||||||
|
// decoding the entire image.
|
||||||
|
// Limitation: The file must be 8, 24 or 32 bits per pixel.
|
||||||
|
func DecodeConfig(r io.Reader) (image.Config, error) {
|
||||||
|
config, _, _, err := decodeConfig(r)
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, err error) {
|
||||||
|
// We only support those BMP images that are a BITMAPFILEHEADER
|
||||||
|
// immediately followed by a BITMAPINFOHEADER.
|
||||||
|
const (
|
||||||
|
fileHeaderLen = 14
|
||||||
|
infoHeaderLen = 40
|
||||||
|
)
|
||||||
|
var b [1024]byte
|
||||||
|
if _, err := io.ReadFull(r, b[:fileHeaderLen+infoHeaderLen]); err != nil {
|
||||||
|
return image.Config{}, 0, false, err
|
||||||
|
}
|
||||||
|
if string(b[:2]) != "BM" {
|
||||||
|
return image.Config{}, 0, false, errors.New("bmp: invalid format")
|
||||||
|
}
|
||||||
|
offset := readUint32(b[10:14])
|
||||||
|
if readUint32(b[14:18]) != infoHeaderLen {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
width := int(int32(readUint32(b[18:22])))
|
||||||
|
height := int(int32(readUint32(b[22:26])))
|
||||||
|
if height < 0 {
|
||||||
|
height, topDown = -height, true
|
||||||
|
}
|
||||||
|
if width < 0 || height < 0 {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
// We only support 1 plane, 8 or 24 bits per pixel and no compression.
|
||||||
|
planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
|
||||||
|
if planes != 1 || compression != 0 {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
switch bpp {
|
||||||
|
case 8:
|
||||||
|
if offset != fileHeaderLen+infoHeaderLen+256*4 {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
_, err = io.ReadFull(r, b[:256*4])
|
||||||
|
if err != nil {
|
||||||
|
return image.Config{}, 0, false, err
|
||||||
|
}
|
||||||
|
pcm := make(color.Palette, 256)
|
||||||
|
for i := range pcm {
|
||||||
|
// BMP images are stored in BGR order rather than RGB order.
|
||||||
|
// Every 4th byte is padding.
|
||||||
|
pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
|
||||||
|
}
|
||||||
|
return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil
|
||||||
|
case 24:
|
||||||
|
if offset != fileHeaderLen+infoHeaderLen {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil
|
||||||
|
case 32:
|
||||||
|
if offset != fileHeaderLen+infoHeaderLen {
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil
|
||||||
|
}
|
||||||
|
return image.Config{}, 0, false, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig)
|
||||||
|
}
|
||||||
166
vendor/golang.org/x/image/bmp/writer.go
generated
vendored
Normal file
166
vendor/golang.org/x/image/bmp/writer.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
sigBM [2]byte
|
||||||
|
fileSize uint32
|
||||||
|
resverved [2]uint16
|
||||||
|
pixOffset uint32
|
||||||
|
dibHeaderSize uint32
|
||||||
|
width uint32
|
||||||
|
height uint32
|
||||||
|
colorPlane uint16
|
||||||
|
bpp uint16
|
||||||
|
compression uint32
|
||||||
|
imageSize uint32
|
||||||
|
xPixelsPerMeter uint32
|
||||||
|
yPixelsPerMeter uint32
|
||||||
|
colorUse uint32
|
||||||
|
colorImportant uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
|
||||||
|
var padding []byte
|
||||||
|
if dx < step {
|
||||||
|
padding = make([]byte, step-dx)
|
||||||
|
}
|
||||||
|
for y := dy - 1; y >= 0; y-- {
|
||||||
|
min := y*stride + 0
|
||||||
|
max := y*stride + dx
|
||||||
|
if _, err := w.Write(pix[min:max]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if padding != nil {
|
||||||
|
if _, err := w.Write(padding); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
|
||||||
|
buf := make([]byte, step)
|
||||||
|
for y := dy - 1; y >= 0; y-- {
|
||||||
|
min := y*stride + 0
|
||||||
|
max := y*stride + dx*4
|
||||||
|
off := 0
|
||||||
|
for i := min; i < max; i += 4 {
|
||||||
|
buf[off+2] = pix[i+0]
|
||||||
|
buf[off+1] = pix[i+1]
|
||||||
|
buf[off+0] = pix[i+2]
|
||||||
|
off += 3
|
||||||
|
}
|
||||||
|
if _, err := w.Write(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(w io.Writer, m image.Image, step int) error {
|
||||||
|
b := m.Bounds()
|
||||||
|
buf := make([]byte, step)
|
||||||
|
for y := b.Max.Y - 1; y >= b.Min.Y; y-- {
|
||||||
|
off := 0
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
r, g, b, _ := m.At(x, y).RGBA()
|
||||||
|
buf[off+2] = byte(r >> 8)
|
||||||
|
buf[off+1] = byte(g >> 8)
|
||||||
|
buf[off+0] = byte(b >> 8)
|
||||||
|
off += 3
|
||||||
|
}
|
||||||
|
if _, err := w.Write(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the image m to w in BMP format.
|
||||||
|
func Encode(w io.Writer, m image.Image) error {
|
||||||
|
d := m.Bounds().Size()
|
||||||
|
if d.X < 0 || d.Y < 0 {
|
||||||
|
return errors.New("bmp: negative bounds")
|
||||||
|
}
|
||||||
|
h := &header{
|
||||||
|
sigBM: [2]byte{'B', 'M'},
|
||||||
|
fileSize: 14 + 40,
|
||||||
|
pixOffset: 14 + 40,
|
||||||
|
dibHeaderSize: 40,
|
||||||
|
width: uint32(d.X),
|
||||||
|
height: uint32(d.Y),
|
||||||
|
colorPlane: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
var step int
|
||||||
|
var palette []byte
|
||||||
|
switch m := m.(type) {
|
||||||
|
case *image.Gray:
|
||||||
|
step = (d.X + 3) &^ 3
|
||||||
|
palette = make([]byte, 1024)
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
palette[i*4+0] = uint8(i)
|
||||||
|
palette[i*4+1] = uint8(i)
|
||||||
|
palette[i*4+2] = uint8(i)
|
||||||
|
palette[i*4+3] = 0xFF
|
||||||
|
}
|
||||||
|
h.imageSize = uint32(d.Y * step)
|
||||||
|
h.fileSize += uint32(len(palette)) + h.imageSize
|
||||||
|
h.pixOffset += uint32(len(palette))
|
||||||
|
h.bpp = 8
|
||||||
|
|
||||||
|
case *image.Paletted:
|
||||||
|
step = (d.X + 3) &^ 3
|
||||||
|
palette = make([]byte, 1024)
|
||||||
|
for i := 0; i < len(m.Palette) && i < 256; i++ {
|
||||||
|
r, g, b, _ := m.Palette[i].RGBA()
|
||||||
|
palette[i*4+0] = uint8(b >> 8)
|
||||||
|
palette[i*4+1] = uint8(g >> 8)
|
||||||
|
palette[i*4+2] = uint8(r >> 8)
|
||||||
|
palette[i*4+3] = 0xFF
|
||||||
|
}
|
||||||
|
h.imageSize = uint32(d.Y * step)
|
||||||
|
h.fileSize += uint32(len(palette)) + h.imageSize
|
||||||
|
h.pixOffset += uint32(len(palette))
|
||||||
|
h.bpp = 8
|
||||||
|
default:
|
||||||
|
step = (3*d.X + 3) &^ 3
|
||||||
|
h.imageSize = uint32(d.Y * step)
|
||||||
|
h.fileSize += h.imageSize
|
||||||
|
h.bpp = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, h); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if palette != nil {
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, palette); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.X == 0 || d.Y == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m := m.(type) {
|
||||||
|
case *image.Gray:
|
||||||
|
return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
|
||||||
|
case *image.Paletted:
|
||||||
|
return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
|
||||||
|
case *image.RGBA:
|
||||||
|
return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step)
|
||||||
|
}
|
||||||
|
return encode(w, m, step)
|
||||||
|
}
|
||||||
43
vendor/golang.org/x/image/draw/draw.go
generated
vendored
Normal file
43
vendor/golang.org/x/image/draw/draw.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package draw provides image composition functions.
|
||||||
|
//
|
||||||
|
// See "The Go image/draw package" for an introduction to this package:
|
||||||
|
// http://golang.org/doc/articles/image_draw.html
|
||||||
|
//
|
||||||
|
// This package is a superset of and a drop-in replacement for the image/draw
|
||||||
|
// package in the standard library.
|
||||||
|
package draw
|
||||||
|
|
||||||
|
// This file, and the go1_*.go files, just contains the API exported by the
|
||||||
|
// image/draw package in the standard library. Other files in this package
|
||||||
|
// provide additional features.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Draw calls DrawMask with a nil mask.
|
||||||
|
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) {
|
||||||
|
draw.Draw(dst, r, src, sp, draw.Op(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then
|
||||||
|
// replaces the rectangle r in dst with the result of a Porter-Duff
|
||||||
|
// composition. A nil mask is treated as opaque.
|
||||||
|
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
|
||||||
|
draw.DrawMask(dst, r, src, sp, mask, mp, draw.Op(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloydSteinberg is a Drawer that is the Src Op with Floyd-Steinberg error
|
||||||
|
// diffusion.
|
||||||
|
var FloydSteinberg Drawer = floydSteinberg{}
|
||||||
|
|
||||||
|
type floydSteinberg struct{}
|
||||||
|
|
||||||
|
func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) {
|
||||||
|
draw.FloydSteinberg.Draw(dst, r, src, sp)
|
||||||
|
}
|
||||||
1404
vendor/golang.org/x/image/draw/gen.go
generated
vendored
Normal file
1404
vendor/golang.org/x/image/draw/gen.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
vendor/golang.org/x/image/draw/go1_8.go
generated
vendored
Normal file
49
vendor/golang.org/x/image/draw/go1_8.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.9,!go1.8.typealias
|
||||||
|
|
||||||
|
package draw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Drawer contains the Draw method.
|
||||||
|
type Drawer interface {
|
||||||
|
// Draw aligns r.Min in dst with sp in src and then replaces the
|
||||||
|
// rectangle r in dst with the result of drawing src on dst.
|
||||||
|
Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image is an image.Image with a Set method to change a single pixel.
|
||||||
|
type Image interface {
|
||||||
|
image.Image
|
||||||
|
Set(x, y int, c color.Color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op is a Porter-Duff compositing operator.
|
||||||
|
type Op int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Over specifies ``(src in mask) over dst''.
|
||||||
|
Over Op = Op(draw.Over)
|
||||||
|
// Src specifies ``src in mask''.
|
||||||
|
Src Op = Op(draw.Src)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Draw implements the Drawer interface by calling the Draw function with
|
||||||
|
// this Op.
|
||||||
|
func (op Op) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) {
|
||||||
|
(draw.Op(op)).Draw(dst, r, src, sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quantizer produces a palette for an image.
|
||||||
|
type Quantizer interface {
|
||||||
|
// Quantize appends up to cap(p) - len(p) colors to p and returns the
|
||||||
|
// updated palette suitable for converting m to a paletted image.
|
||||||
|
Quantize(p color.Palette, m image.Image) color.Palette
|
||||||
|
}
|
||||||
57
vendor/golang.org/x/image/draw/go1_9.go
generated
vendored
Normal file
57
vendor/golang.org/x/image/draw/go1_9.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.9 go1.8.typealias
|
||||||
|
|
||||||
|
package draw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/draw"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We use type aliases (new in Go 1.9) for the exported names from the standard
|
||||||
|
// library's image/draw package. This is not merely syntactic sugar for
|
||||||
|
//
|
||||||
|
// type Drawer draw.Drawer
|
||||||
|
//
|
||||||
|
// as aliasing means that the types in this package, such as draw.Image and
|
||||||
|
// draw.Op, are identical to the corresponding draw.Image and draw.Op types in
|
||||||
|
// the standard library. In comparison, prior to Go 1.9, the code in go1_8.go
|
||||||
|
// defines new types that mimic the old but are different types.
|
||||||
|
//
|
||||||
|
// The package documentation, in draw.go, explicitly gives the intent of this
|
||||||
|
// package:
|
||||||
|
//
|
||||||
|
// This package is a superset of and a drop-in replacement for the
|
||||||
|
// image/draw package in the standard library.
|
||||||
|
//
|
||||||
|
// Drop-in replacement means that I can replace all of my "image/draw" imports
|
||||||
|
// with "golang.org/x/image/draw", to access additional features in this
|
||||||
|
// package, and no further changes are required. That's mostly true, but not
|
||||||
|
// completely true unless we use type aliases.
|
||||||
|
//
|
||||||
|
// Without type aliases, users might need to import both "image/draw" and
|
||||||
|
// "golang.org/x/image/draw" in order to convert from two conceptually
|
||||||
|
// equivalent but different (from the compiler's point of view) types, such as
|
||||||
|
// from one draw.Op type to another draw.Op type, to satisfy some other
|
||||||
|
// interface or function signature.
|
||||||
|
|
||||||
|
// Drawer contains the Draw method.
|
||||||
|
type Drawer = draw.Drawer
|
||||||
|
|
||||||
|
// Image is an image.Image with a Set method to change a single pixel.
|
||||||
|
type Image = draw.Image
|
||||||
|
|
||||||
|
// Op is a Porter-Duff compositing operator.
|
||||||
|
type Op = draw.Op
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Over specifies ``(src in mask) over dst''.
|
||||||
|
Over Op = draw.Over
|
||||||
|
// Src specifies ``src in mask''.
|
||||||
|
Src Op = draw.Src
|
||||||
|
)
|
||||||
|
|
||||||
|
// Quantizer produces a palette for an image.
|
||||||
|
type Quantizer = draw.Quantizer
|
||||||
6670
vendor/golang.org/x/image/draw/impl.go
generated
vendored
Normal file
6670
vendor/golang.org/x/image/draw/impl.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
527
vendor/golang.org/x/image/draw/scale.go
generated
vendored
Normal file
527
vendor/golang.org/x/image/draw/scale.go
generated
vendored
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:generate go run gen.go
|
||||||
|
|
||||||
|
package draw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/f64"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copy copies the part of the source image defined by src and sr and writes
|
||||||
|
// the result of a Porter-Duff composition to the part of the destination image
|
||||||
|
// defined by dst and the translation of sr so that sr.Min translates to dp.
|
||||||
|
func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, op Op, opts *Options) {
|
||||||
|
var o Options
|
||||||
|
if opts != nil {
|
||||||
|
o = *opts
|
||||||
|
}
|
||||||
|
dr := sr.Add(dp.Sub(sr.Min))
|
||||||
|
if o.DstMask == nil {
|
||||||
|
DrawMask(dst, dr, src, sr.Min, o.SrcMask, o.SrcMaskP.Add(sr.Min), op)
|
||||||
|
} else {
|
||||||
|
NearestNeighbor.Scale(dst, dr, src, sr, op, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scaler scales the part of the source image defined by src and sr and writes
|
||||||
|
// the result of a Porter-Duff composition to the part of the destination image
|
||||||
|
// defined by dst and dr.
|
||||||
|
//
|
||||||
|
// A Scaler is safe to use concurrently.
|
||||||
|
type Scaler interface {
|
||||||
|
Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transformer transforms the part of the source image defined by src and sr
|
||||||
|
// and writes the result of a Porter-Duff composition to the part of the
|
||||||
|
// destination image defined by dst and the affine transform m applied to sr.
|
||||||
|
//
|
||||||
|
// For example, if m is the matrix
|
||||||
|
//
|
||||||
|
// m00 m01 m02
|
||||||
|
// m10 m11 m12
|
||||||
|
//
|
||||||
|
// then the src-space point (sx, sy) maps to the dst-space point
|
||||||
|
// (m00*sx + m01*sy + m02, m10*sx + m11*sy + m12).
|
||||||
|
//
|
||||||
|
// A Transformer is safe to use concurrently.
|
||||||
|
type Transformer interface {
|
||||||
|
Transform(dst Image, m f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options are optional parameters to Copy, Scale and Transform.
|
||||||
|
//
|
||||||
|
// A nil *Options means to use the default (zero) values of each field.
|
||||||
|
type Options struct {
|
||||||
|
// Masks limit what parts of the dst image are drawn to and what parts of
|
||||||
|
// the src image are drawn from.
|
||||||
|
//
|
||||||
|
// A dst or src mask image having a zero alpha (transparent) pixel value in
|
||||||
|
// the respective coordinate space means that that dst pixel is entirely
|
||||||
|
// unaffected or that src pixel is considered transparent black. A full
|
||||||
|
// alpha (opaque) value means that the dst pixel is maximally affected or
|
||||||
|
// the src pixel contributes maximally. The default values, nil, are
|
||||||
|
// equivalent to fully opaque, infinitely large mask images.
|
||||||
|
//
|
||||||
|
// The DstMask is otherwise known as a clip mask, and its pixels map 1:1 to
|
||||||
|
// the dst image's pixels. DstMaskP in DstMask space corresponds to
|
||||||
|
// image.Point{X:0, Y:0} in dst space. For example, when limiting
|
||||||
|
// repainting to a 'dirty rectangle', use that image.Rectangle and a zero
|
||||||
|
// image.Point as the DstMask and DstMaskP.
|
||||||
|
//
|
||||||
|
// The SrcMask's pixels map 1:1 to the src image's pixels. SrcMaskP in
|
||||||
|
// SrcMask space corresponds to image.Point{X:0, Y:0} in src space. For
|
||||||
|
// example, when drawing font glyphs in a uniform color, use an
|
||||||
|
// *image.Uniform as the src, and use the glyph atlas image and the
|
||||||
|
// per-glyph offset as SrcMask and SrcMaskP:
|
||||||
|
// Copy(dst, dp, image.NewUniform(color), image.Rect(0, 0, glyphWidth, glyphHeight), &Options{
|
||||||
|
// SrcMask: glyphAtlas,
|
||||||
|
// SrcMaskP: glyphOffset,
|
||||||
|
// })
|
||||||
|
DstMask image.Image
|
||||||
|
DstMaskP image.Point
|
||||||
|
SrcMask image.Image
|
||||||
|
SrcMaskP image.Point
|
||||||
|
|
||||||
|
// TODO: a smooth vs sharp edges option, for arbitrary rotations?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolator is an interpolation algorithm, when dst and src pixels don't
|
||||||
|
// have a 1:1 correspondence.
|
||||||
|
//
|
||||||
|
// Of the interpolators provided by this package:
|
||||||
|
// - NearestNeighbor is fast but usually looks worst.
|
||||||
|
// - CatmullRom is slow but usually looks best.
|
||||||
|
// - ApproxBiLinear has reasonable speed and quality.
|
||||||
|
//
|
||||||
|
// The time taken depends on the size of dr. For kernel interpolators, the
|
||||||
|
// speed also depends on the size of sr, and so are often slower than
|
||||||
|
// non-kernel interpolators, especially when scaling down.
|
||||||
|
type Interpolator interface {
|
||||||
|
Scaler
|
||||||
|
Transformer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel is an interpolator that blends source pixels weighted by a symmetric
|
||||||
|
// kernel function.
|
||||||
|
type Kernel struct {
|
||||||
|
// Support is the kernel support and must be >= 0. At(t) is assumed to be
|
||||||
|
// zero when t >= Support.
|
||||||
|
Support float64
|
||||||
|
// At is the kernel function. It will only be called with t in the
|
||||||
|
// range [0, Support).
|
||||||
|
At func(t float64) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale implements the Scaler interface.
|
||||||
|
func (q *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
|
||||||
|
q.newScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy(), false).Scale(dst, dr, src, sr, op, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScaler returns a Scaler that is optimized for scaling multiple times with
|
||||||
|
// the same fixed destination and source width and height.
|
||||||
|
func (q *Kernel) NewScaler(dw, dh, sw, sh int) Scaler {
|
||||||
|
return q.newScaler(dw, dh, sw, sh, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Kernel) newScaler(dw, dh, sw, sh int, usePool bool) Scaler {
|
||||||
|
z := &kernelScaler{
|
||||||
|
kernel: q,
|
||||||
|
dw: int32(dw),
|
||||||
|
dh: int32(dh),
|
||||||
|
sw: int32(sw),
|
||||||
|
sh: int32(sh),
|
||||||
|
horizontal: newDistrib(q, int32(dw), int32(sw)),
|
||||||
|
vertical: newDistrib(q, int32(dh), int32(sh)),
|
||||||
|
}
|
||||||
|
if usePool {
|
||||||
|
z.pool.New = func() interface{} {
|
||||||
|
tmp := z.makeTmpBuf()
|
||||||
|
return &tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NearestNeighbor is the nearest neighbor interpolator. It is very fast,
|
||||||
|
// but usually gives very low quality results. When scaling up, the result
|
||||||
|
// will look 'blocky'.
|
||||||
|
NearestNeighbor = Interpolator(nnInterpolator{})
|
||||||
|
|
||||||
|
// ApproxBiLinear is a mixture of the nearest neighbor and bi-linear
|
||||||
|
// interpolators. It is fast, but usually gives medium quality results.
|
||||||
|
//
|
||||||
|
// It implements bi-linear interpolation when upscaling and a bi-linear
|
||||||
|
// blend of the 4 nearest neighbor pixels when downscaling. This yields
|
||||||
|
// nicer quality than nearest neighbor interpolation when upscaling, but
|
||||||
|
// the time taken is independent of the number of source pixels, unlike the
|
||||||
|
// bi-linear interpolator. When downscaling a large image, the performance
|
||||||
|
// difference can be significant.
|
||||||
|
ApproxBiLinear = Interpolator(ablInterpolator{})
|
||||||
|
|
||||||
|
// BiLinear is the tent kernel. It is slow, but usually gives high quality
|
||||||
|
// results.
|
||||||
|
BiLinear = &Kernel{1, func(t float64) float64 {
|
||||||
|
return 1 - t
|
||||||
|
}}
|
||||||
|
|
||||||
|
// CatmullRom is the Catmull-Rom kernel. It is very slow, but usually gives
|
||||||
|
// very high quality results.
|
||||||
|
//
|
||||||
|
// It is an instance of the more general cubic BC-spline kernel with parameters
|
||||||
|
// B=0 and C=0.5. See Mitchell and Netravali, "Reconstruction Filters in
|
||||||
|
// Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228.
|
||||||
|
CatmullRom = &Kernel{2, func(t float64) float64 {
|
||||||
|
if t < 1 {
|
||||||
|
return (1.5*t-2.5)*t*t + 1
|
||||||
|
}
|
||||||
|
return ((-0.5*t+2.5)*t-4)*t + 2
|
||||||
|
}}
|
||||||
|
|
||||||
|
// TODO: a Kaiser-Bessel kernel?
|
||||||
|
)
|
||||||
|
|
||||||
|
type nnInterpolator struct{}
|
||||||
|
|
||||||
|
type ablInterpolator struct{}
|
||||||
|
|
||||||
|
type kernelScaler struct {
|
||||||
|
kernel *Kernel
|
||||||
|
dw, dh, sw, sh int32
|
||||||
|
horizontal, vertical distrib
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *kernelScaler) makeTmpBuf() [][4]float64 {
|
||||||
|
return make([][4]float64, z.dw*z.sh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// source is a range of contribs, their inverse total weight, and that ITW
|
||||||
|
// divided by 0xffff.
|
||||||
|
type source struct {
|
||||||
|
i, j int32
|
||||||
|
invTotalWeight float64
|
||||||
|
invTotalWeightFFFF float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// contrib is the weight of a column or row.
|
||||||
|
type contrib struct {
|
||||||
|
coord int32
|
||||||
|
weight float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// distrib measures how source pixels are distributed over destination pixels.
|
||||||
|
type distrib struct {
|
||||||
|
// sources are what contribs each column or row in the source image owns,
|
||||||
|
// and the total weight of those contribs.
|
||||||
|
sources []source
|
||||||
|
// contribs are the contributions indexed by sources[s].i and sources[s].j.
|
||||||
|
contribs []contrib
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDistrib returns a distrib that distributes sw source columns (or rows)
|
||||||
|
// over dw destination columns (or rows).
|
||||||
|
func newDistrib(q *Kernel, dw, sw int32) distrib {
|
||||||
|
scale := float64(sw) / float64(dw)
|
||||||
|
halfWidth, kernelArgScale := q.Support, 1.0
|
||||||
|
// When shrinking, broaden the effective kernel support so that we still
|
||||||
|
// visit every source pixel.
|
||||||
|
if scale > 1 {
|
||||||
|
halfWidth *= scale
|
||||||
|
kernelArgScale = 1 / scale
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the sources slice, one source for each column or row, and temporarily
|
||||||
|
// appropriate its elements' fields so that invTotalWeight is the scaled
|
||||||
|
// coordinate of the source column or row, and i and j are the lower and
|
||||||
|
// upper bounds of the range of destination columns or rows affected by the
|
||||||
|
// source column or row.
|
||||||
|
n, sources := int32(0), make([]source, dw)
|
||||||
|
for x := range sources {
|
||||||
|
center := (float64(x)+0.5)*scale - 0.5
|
||||||
|
i := int32(math.Floor(center - halfWidth))
|
||||||
|
if i < 0 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
j := int32(math.Ceil(center + halfWidth))
|
||||||
|
if j > sw {
|
||||||
|
j = sw
|
||||||
|
if j < i {
|
||||||
|
j = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sources[x] = source{i: i, j: j, invTotalWeight: center}
|
||||||
|
n += j - i
|
||||||
|
}
|
||||||
|
|
||||||
|
contribs := make([]contrib, 0, n)
|
||||||
|
for k, b := range sources {
|
||||||
|
totalWeight := 0.0
|
||||||
|
l := int32(len(contribs))
|
||||||
|
for coord := b.i; coord < b.j; coord++ {
|
||||||
|
t := abs((b.invTotalWeight - float64(coord)) * kernelArgScale)
|
||||||
|
if t >= q.Support {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
weight := q.At(t)
|
||||||
|
if weight == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalWeight += weight
|
||||||
|
contribs = append(contribs, contrib{coord, weight})
|
||||||
|
}
|
||||||
|
totalWeight = 1 / totalWeight
|
||||||
|
sources[k] = source{
|
||||||
|
i: l,
|
||||||
|
j: int32(len(contribs)),
|
||||||
|
invTotalWeight: totalWeight,
|
||||||
|
invTotalWeightFFFF: totalWeight / 0xffff,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distrib{sources, contribs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// abs is like math.Abs, but it doesn't care about negative zero, infinities or
|
||||||
|
// NaNs.
|
||||||
|
func abs(f float64) float64 {
|
||||||
|
if f < 0 {
|
||||||
|
f = -f
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// ftou converts the range [0.0, 1.0] to [0, 0xffff].
|
||||||
|
func ftou(f float64) uint16 {
|
||||||
|
i := int32(0xffff*f + 0.5)
|
||||||
|
if i > 0xffff {
|
||||||
|
return 0xffff
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
return uint16(i)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// fffftou converts the range [0.0, 65535.0] to [0, 0xffff].
|
||||||
|
func fffftou(f float64) uint16 {
|
||||||
|
i := int32(f + 0.5)
|
||||||
|
if i > 0xffff {
|
||||||
|
return 0xffff
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
return uint16(i)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// invert returns the inverse of m.
|
||||||
|
//
|
||||||
|
// TODO: move this into the f64 package, once we work out the convention for
|
||||||
|
// matrix methods in that package: do they modify the receiver, take a dst
|
||||||
|
// pointer argument, or return a new value?
|
||||||
|
func invert(m *f64.Aff3) f64.Aff3 {
|
||||||
|
m00 := +m[3*1+1]
|
||||||
|
m01 := -m[3*0+1]
|
||||||
|
m02 := +m[3*1+2]*m[3*0+1] - m[3*1+1]*m[3*0+2]
|
||||||
|
m10 := -m[3*1+0]
|
||||||
|
m11 := +m[3*0+0]
|
||||||
|
m12 := +m[3*1+0]*m[3*0+2] - m[3*1+2]*m[3*0+0]
|
||||||
|
|
||||||
|
det := m00*m11 - m10*m01
|
||||||
|
|
||||||
|
return f64.Aff3{
|
||||||
|
m00 / det,
|
||||||
|
m01 / det,
|
||||||
|
m02 / det,
|
||||||
|
m10 / det,
|
||||||
|
m11 / det,
|
||||||
|
m12 / det,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func matMul(p, q *f64.Aff3) f64.Aff3 {
|
||||||
|
return f64.Aff3{
|
||||||
|
p[3*0+0]*q[3*0+0] + p[3*0+1]*q[3*1+0],
|
||||||
|
p[3*0+0]*q[3*0+1] + p[3*0+1]*q[3*1+1],
|
||||||
|
p[3*0+0]*q[3*0+2] + p[3*0+1]*q[3*1+2] + p[3*0+2],
|
||||||
|
p[3*1+0]*q[3*0+0] + p[3*1+1]*q[3*1+0],
|
||||||
|
p[3*1+0]*q[3*0+1] + p[3*1+1]*q[3*1+1],
|
||||||
|
p[3*1+0]*q[3*0+2] + p[3*1+1]*q[3*1+2] + p[3*1+2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformRect returns a rectangle dr that contains sr transformed by s2d.
|
||||||
|
func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
|
||||||
|
ps := [...]image.Point{
|
||||||
|
{sr.Min.X, sr.Min.Y},
|
||||||
|
{sr.Max.X, sr.Min.Y},
|
||||||
|
{sr.Min.X, sr.Max.Y},
|
||||||
|
{sr.Max.X, sr.Max.Y},
|
||||||
|
}
|
||||||
|
for i, p := range ps {
|
||||||
|
sxf := float64(p.X)
|
||||||
|
syf := float64(p.Y)
|
||||||
|
dx := int(math.Floor(s2d[0]*sxf + s2d[1]*syf + s2d[2]))
|
||||||
|
dy := int(math.Floor(s2d[3]*sxf + s2d[4]*syf + s2d[5]))
|
||||||
|
|
||||||
|
// The +1 adjustments below are because an image.Rectangle is inclusive
|
||||||
|
// on the low end but exclusive on the high end.
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
dr = image.Rectangle{
|
||||||
|
Min: image.Point{dx + 0, dy + 0},
|
||||||
|
Max: image.Point{dx + 1, dy + 1},
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if dr.Min.X > dx {
|
||||||
|
dr.Min.X = dx
|
||||||
|
}
|
||||||
|
dx++
|
||||||
|
if dr.Max.X < dx {
|
||||||
|
dr.Max.X = dx
|
||||||
|
}
|
||||||
|
|
||||||
|
if dr.Min.Y > dy {
|
||||||
|
dr.Min.Y = dy
|
||||||
|
}
|
||||||
|
dy++
|
||||||
|
if dr.Max.Y < dy {
|
||||||
|
dr.Max.Y = dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dr
|
||||||
|
}
|
||||||
|
|
||||||
|
func clipAffectedDestRect(adr image.Rectangle, dstMask image.Image, dstMaskP image.Point) (image.Rectangle, image.Image) {
|
||||||
|
if dstMask == nil {
|
||||||
|
return adr, nil
|
||||||
|
}
|
||||||
|
// TODO: enable this fast path once Go 1.5 is released, where an
|
||||||
|
// image.Rectangle implements image.Image.
|
||||||
|
// if r, ok := dstMask.(image.Rectangle); ok {
|
||||||
|
// return adr.Intersect(r.Sub(dstMaskP)), nil
|
||||||
|
// }
|
||||||
|
// TODO: clip to dstMask.Bounds() if the color model implies that out-of-bounds means 0 alpha?
|
||||||
|
return adr, dstMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Uniform, sr image.Rectangle, bias image.Point, op Op) {
|
||||||
|
switch op {
|
||||||
|
case Over:
|
||||||
|
switch dst := dst.(type) {
|
||||||
|
case *image.RGBA:
|
||||||
|
pr, pg, pb, pa := src.C.RGBA()
|
||||||
|
pa1 := (0xffff - pa) * 0x101
|
||||||
|
|
||||||
|
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
|
||||||
|
dyf := float64(dr.Min.Y+int(dy)) + 0.5
|
||||||
|
d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
|
||||||
|
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
|
||||||
|
dxf := float64(dr.Min.X+int(dx)) + 0.5
|
||||||
|
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
|
||||||
|
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
|
||||||
|
if !(image.Point{sx0, sy0}).In(sr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dst.Pix[d+0] = uint8((uint32(dst.Pix[d+0])*pa1/0xffff + pr) >> 8)
|
||||||
|
dst.Pix[d+1] = uint8((uint32(dst.Pix[d+1])*pa1/0xffff + pg) >> 8)
|
||||||
|
dst.Pix[d+2] = uint8((uint32(dst.Pix[d+2])*pa1/0xffff + pb) >> 8)
|
||||||
|
dst.Pix[d+3] = uint8((uint32(dst.Pix[d+3])*pa1/0xffff + pa) >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr, pg, pb, pa := src.C.RGBA()
|
||||||
|
pa1 := 0xffff - pa
|
||||||
|
dstColorRGBA64 := &color.RGBA64{}
|
||||||
|
dstColor := color.Color(dstColorRGBA64)
|
||||||
|
|
||||||
|
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
|
||||||
|
dyf := float64(dr.Min.Y+int(dy)) + 0.5
|
||||||
|
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
|
||||||
|
dxf := float64(dr.Min.X+int(dx)) + 0.5
|
||||||
|
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
|
||||||
|
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
|
||||||
|
if !(image.Point{sx0, sy0}).In(sr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
|
||||||
|
dstColorRGBA64.R = uint16(qr*pa1/0xffff + pr)
|
||||||
|
dstColorRGBA64.G = uint16(qg*pa1/0xffff + pg)
|
||||||
|
dstColorRGBA64.B = uint16(qb*pa1/0xffff + pb)
|
||||||
|
dstColorRGBA64.A = uint16(qa*pa1/0xffff + pa)
|
||||||
|
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case Src:
|
||||||
|
switch dst := dst.(type) {
|
||||||
|
case *image.RGBA:
|
||||||
|
pr, pg, pb, pa := src.C.RGBA()
|
||||||
|
pr8 := uint8(pr >> 8)
|
||||||
|
pg8 := uint8(pg >> 8)
|
||||||
|
pb8 := uint8(pb >> 8)
|
||||||
|
pa8 := uint8(pa >> 8)
|
||||||
|
|
||||||
|
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
|
||||||
|
dyf := float64(dr.Min.Y+int(dy)) + 0.5
|
||||||
|
d := dst.PixOffset(dr.Min.X+adr.Min.X, dr.Min.Y+int(dy))
|
||||||
|
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx, d = dx+1, d+4 {
|
||||||
|
dxf := float64(dr.Min.X+int(dx)) + 0.5
|
||||||
|
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
|
||||||
|
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
|
||||||
|
if !(image.Point{sx0, sy0}).In(sr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dst.Pix[d+0] = pr8
|
||||||
|
dst.Pix[d+1] = pg8
|
||||||
|
dst.Pix[d+2] = pb8
|
||||||
|
dst.Pix[d+3] = pa8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr, pg, pb, pa := src.C.RGBA()
|
||||||
|
dstColorRGBA64 := &color.RGBA64{
|
||||||
|
uint16(pr),
|
||||||
|
uint16(pg),
|
||||||
|
uint16(pb),
|
||||||
|
uint16(pa),
|
||||||
|
}
|
||||||
|
dstColor := color.Color(dstColorRGBA64)
|
||||||
|
|
||||||
|
for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
|
||||||
|
dyf := float64(dr.Min.Y+int(dy)) + 0.5
|
||||||
|
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
|
||||||
|
dxf := float64(dr.Min.X+int(dx)) + 0.5
|
||||||
|
sx0 := int(d2s[0]*dxf+d2s[1]*dyf+d2s[2]) + bias.X
|
||||||
|
sy0 := int(d2s[3]*dxf+d2s[4]*dyf+d2s[5]) + bias.Y
|
||||||
|
if !(image.Point{sx0, sy0}).In(sr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dst.Set(dr.Min.X+int(dx), dr.Min.Y+int(dy), dstColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func opaque(m image.Image) bool {
|
||||||
|
o, ok := m.(interface {
|
||||||
|
Opaque() bool
|
||||||
|
})
|
||||||
|
return ok && o.Opaque()
|
||||||
|
}
|
||||||
37
vendor/golang.org/x/image/math/f64/f64.go
generated
vendored
Normal file
37
vendor/golang.org/x/image/math/f64/f64.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package f64 implements float64 vector and matrix types.
|
||||||
|
package f64 // import "golang.org/x/image/math/f64"
|
||||||
|
|
||||||
|
// Vec2 is a 2-element vector.
|
||||||
|
type Vec2 [2]float64
|
||||||
|
|
||||||
|
// Vec3 is a 3-element vector.
|
||||||
|
type Vec3 [3]float64
|
||||||
|
|
||||||
|
// Vec4 is a 4-element vector.
|
||||||
|
type Vec4 [4]float64
|
||||||
|
|
||||||
|
// Mat3 is a 3x3 matrix in row major order.
|
||||||
|
//
|
||||||
|
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||||
|
type Mat3 [9]float64
|
||||||
|
|
||||||
|
// Mat4 is a 4x4 matrix in row major order.
|
||||||
|
//
|
||||||
|
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||||
|
type Mat4 [16]float64
|
||||||
|
|
||||||
|
// Aff3 is a 3x3 affine transformation matrix in row major order, where the
|
||||||
|
// bottom row is implicitly [0 0 1].
|
||||||
|
//
|
||||||
|
// m[3*r + c] is the element in the r'th row and c'th column.
|
||||||
|
type Aff3 [6]float64
|
||||||
|
|
||||||
|
// Aff4 is a 4x4 affine transformation matrix in row major order, where the
|
||||||
|
// bottom row is implicitly [0 0 0 1].
|
||||||
|
//
|
||||||
|
// m[4*r + c] is the element in the r'th row and c'th column.
|
||||||
|
type Aff4 [12]float64
|
||||||
193
vendor/golang.org/x/image/riff/riff.go
generated
vendored
Normal file
193
vendor/golang.org/x/image/riff/riff.go
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package riff implements the Resource Interchange File Format, used by media
|
||||||
|
// formats such as AVI, WAVE and WEBP.
|
||||||
|
//
|
||||||
|
// A RIFF stream contains a sequence of chunks. Each chunk consists of an 8-byte
|
||||||
|
// header (containing a 4-byte chunk type and a 4-byte chunk length), the chunk
|
||||||
|
// data (presented as an io.Reader), and some padding bytes.
|
||||||
|
//
|
||||||
|
// A detailed description of the format is at
|
||||||
|
// http://www.tactilemedia.com/info/MCI_Control_Info.html
|
||||||
|
package riff // import "golang.org/x/image/riff"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingPaddingByte = errors.New("riff: missing padding byte")
|
||||||
|
errMissingRIFFChunkHeader = errors.New("riff: missing RIFF chunk header")
|
||||||
|
errListSubchunkTooLong = errors.New("riff: list subchunk too long")
|
||||||
|
errShortChunkData = errors.New("riff: short chunk data")
|
||||||
|
errShortChunkHeader = errors.New("riff: short chunk header")
|
||||||
|
errStaleReader = errors.New("riff: stale reader")
|
||||||
|
)
|
||||||
|
|
||||||
|
// u32 decodes the first four bytes of b as a little-endian integer.
|
||||||
|
func u32(b []byte) uint32 {
|
||||||
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunkHeaderSize = 8
|
||||||
|
|
||||||
|
// FourCC is a four character code.
|
||||||
|
type FourCC [4]byte
|
||||||
|
|
||||||
|
// LIST is the "LIST" FourCC.
|
||||||
|
var LIST = FourCC{'L', 'I', 'S', 'T'}
|
||||||
|
|
||||||
|
// NewReader returns the RIFF stream's form type, such as "AVI " or "WAVE", and
|
||||||
|
// its chunks as a *Reader.
|
||||||
|
func NewReader(r io.Reader) (formType FourCC, data *Reader, err error) {
|
||||||
|
var buf [chunkHeaderSize]byte
|
||||||
|
if _, err := io.ReadFull(r, buf[:]); err != nil {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
|
err = errMissingRIFFChunkHeader
|
||||||
|
}
|
||||||
|
return FourCC{}, nil, err
|
||||||
|
}
|
||||||
|
if buf[0] != 'R' || buf[1] != 'I' || buf[2] != 'F' || buf[3] != 'F' {
|
||||||
|
return FourCC{}, nil, errMissingRIFFChunkHeader
|
||||||
|
}
|
||||||
|
return NewListReader(u32(buf[4:]), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListReader returns a LIST chunk's list type, such as "movi" or "wavl",
|
||||||
|
// and its chunks as a *Reader.
|
||||||
|
func NewListReader(chunkLen uint32, chunkData io.Reader) (listType FourCC, data *Reader, err error) {
|
||||||
|
if chunkLen < 4 {
|
||||||
|
return FourCC{}, nil, errShortChunkData
|
||||||
|
}
|
||||||
|
z := &Reader{r: chunkData}
|
||||||
|
if _, err := io.ReadFull(chunkData, z.buf[:4]); err != nil {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
|
err = errShortChunkData
|
||||||
|
}
|
||||||
|
return FourCC{}, nil, err
|
||||||
|
}
|
||||||
|
z.totalLen = chunkLen - 4
|
||||||
|
return FourCC{z.buf[0], z.buf[1], z.buf[2], z.buf[3]}, z, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader reads chunks from an underlying io.Reader.
|
||||||
|
type Reader struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
|
||||||
|
totalLen uint32
|
||||||
|
chunkLen uint32
|
||||||
|
|
||||||
|
chunkReader *chunkReader
|
||||||
|
buf [chunkHeaderSize]byte
|
||||||
|
padded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next chunk's ID, length and data. It returns io.EOF if there
|
||||||
|
// are no more chunks. The io.Reader returned becomes stale after the next Next
|
||||||
|
// call, and should no longer be used.
|
||||||
|
//
|
||||||
|
// It is valid to call Next even if all of the previous chunk's data has not
|
||||||
|
// been read.
|
||||||
|
func (z *Reader) Next() (chunkID FourCC, chunkLen uint32, chunkData io.Reader, err error) {
|
||||||
|
if z.err != nil {
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drain the rest of the previous chunk.
|
||||||
|
if z.chunkLen != 0 {
|
||||||
|
want := z.chunkLen
|
||||||
|
var got int64
|
||||||
|
got, z.err = io.Copy(ioutil.Discard, z.chunkReader)
|
||||||
|
if z.err == nil && uint32(got) != want {
|
||||||
|
z.err = errShortChunkData
|
||||||
|
}
|
||||||
|
if z.err != nil {
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
z.chunkReader = nil
|
||||||
|
if z.padded {
|
||||||
|
if z.totalLen == 0 {
|
||||||
|
z.err = errListSubchunkTooLong
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
z.totalLen--
|
||||||
|
_, z.err = io.ReadFull(z.r, z.buf[:1])
|
||||||
|
if z.err != nil {
|
||||||
|
if z.err == io.EOF {
|
||||||
|
z.err = errMissingPaddingByte
|
||||||
|
}
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are done if we have no more data.
|
||||||
|
if z.totalLen == 0 {
|
||||||
|
z.err = io.EOF
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next chunk header.
|
||||||
|
if z.totalLen < chunkHeaderSize {
|
||||||
|
z.err = errShortChunkHeader
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
z.totalLen -= chunkHeaderSize
|
||||||
|
if _, z.err = io.ReadFull(z.r, z.buf[:chunkHeaderSize]); z.err != nil {
|
||||||
|
if z.err == io.EOF || z.err == io.ErrUnexpectedEOF {
|
||||||
|
z.err = errShortChunkHeader
|
||||||
|
}
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
chunkID = FourCC{z.buf[0], z.buf[1], z.buf[2], z.buf[3]}
|
||||||
|
z.chunkLen = u32(z.buf[4:])
|
||||||
|
if z.chunkLen > z.totalLen {
|
||||||
|
z.err = errListSubchunkTooLong
|
||||||
|
return FourCC{}, 0, nil, z.err
|
||||||
|
}
|
||||||
|
z.padded = z.chunkLen&1 == 1
|
||||||
|
z.chunkReader = &chunkReader{z}
|
||||||
|
return chunkID, z.chunkLen, z.chunkReader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type chunkReader struct {
|
||||||
|
z *Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chunkReader) Read(p []byte) (int, error) {
|
||||||
|
if c != c.z.chunkReader {
|
||||||
|
return 0, errStaleReader
|
||||||
|
}
|
||||||
|
z := c.z
|
||||||
|
if z.err != nil {
|
||||||
|
if z.err == io.EOF {
|
||||||
|
return 0, errStaleReader
|
||||||
|
}
|
||||||
|
return 0, z.err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := int(z.chunkLen)
|
||||||
|
if n == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if n < 0 {
|
||||||
|
// Converting uint32 to int overflowed.
|
||||||
|
n = math.MaxInt32
|
||||||
|
}
|
||||||
|
if n > len(p) {
|
||||||
|
n = len(p)
|
||||||
|
}
|
||||||
|
n, err := z.r.Read(p[:n])
|
||||||
|
z.totalLen -= uint32(n)
|
||||||
|
z.chunkLen -= uint32(n)
|
||||||
|
if err != io.EOF {
|
||||||
|
z.err = err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
403
vendor/golang.org/x/image/vp8/decode.go
generated
vendored
Normal file
403
vendor/golang.org/x/image/vp8/decode.go
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package vp8 implements a decoder for the VP8 lossy image format.
|
||||||
|
//
|
||||||
|
// The VP8 specification is RFC 6386.
|
||||||
|
package vp8 // import "golang.org/x/image/vp8"
|
||||||
|
|
||||||
|
// This file implements the top-level decoding algorithm.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// limitReader wraps an io.Reader to read at most n bytes from it.
|
||||||
|
type limitReader struct {
|
||||||
|
r io.Reader
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFull reads exactly len(p) bytes into p.
|
||||||
|
func (r *limitReader) ReadFull(p []byte) error {
|
||||||
|
if len(p) > r.n {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
n, err := io.ReadFull(r.r, p)
|
||||||
|
r.n -= n
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameHeader is a frame header, as specified in section 9.1.
|
||||||
|
type FrameHeader struct {
|
||||||
|
KeyFrame bool
|
||||||
|
VersionNumber uint8
|
||||||
|
ShowFrame bool
|
||||||
|
FirstPartitionLen uint32
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
XScale uint8
|
||||||
|
YScale uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
nSegment = 4
|
||||||
|
nSegmentProb = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// segmentHeader holds segment-related header information.
|
||||||
|
type segmentHeader struct {
|
||||||
|
useSegment bool
|
||||||
|
updateMap bool
|
||||||
|
relativeDelta bool
|
||||||
|
quantizer [nSegment]int8
|
||||||
|
filterStrength [nSegment]int8
|
||||||
|
prob [nSegmentProb]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
nRefLFDelta = 4
|
||||||
|
nModeLFDelta = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// filterHeader holds filter-related header information.
|
||||||
|
type filterHeader struct {
|
||||||
|
simple bool
|
||||||
|
level int8
|
||||||
|
sharpness uint8
|
||||||
|
useLFDelta bool
|
||||||
|
refLFDelta [nRefLFDelta]int8
|
||||||
|
modeLFDelta [nModeLFDelta]int8
|
||||||
|
perSegmentLevel [nSegment]int8
|
||||||
|
}
|
||||||
|
|
||||||
|
// mb is the per-macroblock decode state. A decoder maintains mbw+1 of these
|
||||||
|
// as it is decoding macroblocks left-to-right and top-to-bottom: mbw for the
|
||||||
|
// macroblocks in the row above, and one for the macroblock to the left.
|
||||||
|
type mb struct {
|
||||||
|
// pred is the predictor mode for the 4 bottom or right 4x4 luma regions.
|
||||||
|
pred [4]uint8
|
||||||
|
// nzMask is a mask of 8 bits: 4 for the bottom or right 4x4 luma regions,
|
||||||
|
// and 2 + 2 for the bottom or right 4x4 chroma regions. A 1 bit indicates
|
||||||
|
// that that region has non-zero coefficients.
|
||||||
|
nzMask uint8
|
||||||
|
// nzY16 is a 0/1 value that is 1 if the macroblock used Y16 prediction and
|
||||||
|
// had non-zero coefficients.
|
||||||
|
nzY16 uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder decodes VP8 bitstreams into frames. Decoding one frame consists of
|
||||||
|
// calling Init, DecodeFrameHeader and then DecodeFrame in that order.
|
||||||
|
// A Decoder can be re-used to decode multiple frames.
|
||||||
|
type Decoder struct {
|
||||||
|
// r is the input bitsream.
|
||||||
|
r limitReader
|
||||||
|
// scratch is a scratch buffer.
|
||||||
|
scratch [8]byte
|
||||||
|
// img is the YCbCr image to decode into.
|
||||||
|
img *image.YCbCr
|
||||||
|
// mbw and mbh are the number of 16x16 macroblocks wide and high the image is.
|
||||||
|
mbw, mbh int
|
||||||
|
// frameHeader is the frame header. When decoding multiple frames,
|
||||||
|
// frames that aren't key frames will inherit the Width, Height,
|
||||||
|
// XScale and YScale of the most recent key frame.
|
||||||
|
frameHeader FrameHeader
|
||||||
|
// Other headers.
|
||||||
|
segmentHeader segmentHeader
|
||||||
|
filterHeader filterHeader
|
||||||
|
// The image data is divided into a number of independent partitions.
|
||||||
|
// There is 1 "first partition" and between 1 and 8 "other partitions"
|
||||||
|
// for coefficient data.
|
||||||
|
fp partition
|
||||||
|
op [8]partition
|
||||||
|
nOP int
|
||||||
|
// Quantization factors.
|
||||||
|
quant [nSegment]quant
|
||||||
|
// DCT/WHT coefficient decoding probabilities.
|
||||||
|
tokenProb [nPlane][nBand][nContext][nProb]uint8
|
||||||
|
useSkipProb bool
|
||||||
|
skipProb uint8
|
||||||
|
// Loop filter parameters.
|
||||||
|
filterParams [nSegment][2]filterParam
|
||||||
|
perMBFilterParams []filterParam
|
||||||
|
|
||||||
|
// The eight fields below relate to the current macroblock being decoded.
|
||||||
|
//
|
||||||
|
// Segment-based adjustments.
|
||||||
|
segment int
|
||||||
|
// Per-macroblock state for the macroblock immediately left of and those
|
||||||
|
// macroblocks immediately above the current macroblock.
|
||||||
|
leftMB mb
|
||||||
|
upMB []mb
|
||||||
|
// Bitmasks for which 4x4 regions of coeff contain non-zero coefficients.
|
||||||
|
nzDCMask, nzACMask uint32
|
||||||
|
// Predictor modes.
|
||||||
|
usePredY16 bool // The libwebp C code calls this !is_i4x4_.
|
||||||
|
predY16 uint8
|
||||||
|
predC8 uint8
|
||||||
|
predY4 [4][4]uint8
|
||||||
|
|
||||||
|
// The two fields below form a workspace for reconstructing a macroblock.
|
||||||
|
// Their specific sizes are documented in reconstruct.go.
|
||||||
|
coeff [1*16*16 + 2*8*8 + 1*4*4]int16
|
||||||
|
ybr [1 + 16 + 1 + 8][32]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new Decoder.
|
||||||
|
func NewDecoder() *Decoder {
|
||||||
|
return &Decoder{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the decoder to read at most n bytes from r.
|
||||||
|
func (d *Decoder) Init(r io.Reader, n int) {
|
||||||
|
d.r = limitReader{r, n}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFrameHeader decodes the frame header.
|
||||||
|
func (d *Decoder) DecodeFrameHeader() (fh FrameHeader, err error) {
|
||||||
|
// All frame headers are at least 3 bytes long.
|
||||||
|
b := d.scratch[:3]
|
||||||
|
if err = d.r.ReadFull(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.frameHeader.KeyFrame = (b[0] & 1) == 0
|
||||||
|
d.frameHeader.VersionNumber = (b[0] >> 1) & 7
|
||||||
|
d.frameHeader.ShowFrame = (b[0]>>4)&1 == 1
|
||||||
|
d.frameHeader.FirstPartitionLen = uint32(b[0])>>5 | uint32(b[1])<<3 | uint32(b[2])<<11
|
||||||
|
if !d.frameHeader.KeyFrame {
|
||||||
|
return d.frameHeader, nil
|
||||||
|
}
|
||||||
|
// Frame headers for key frames are an additional 7 bytes long.
|
||||||
|
b = d.scratch[:7]
|
||||||
|
if err = d.r.ReadFull(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check the magic sync code.
|
||||||
|
if b[0] != 0x9d || b[1] != 0x01 || b[2] != 0x2a {
|
||||||
|
err = errors.New("vp8: invalid format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.frameHeader.Width = int(b[4]&0x3f)<<8 | int(b[3])
|
||||||
|
d.frameHeader.Height = int(b[6]&0x3f)<<8 | int(b[5])
|
||||||
|
d.frameHeader.XScale = b[4] >> 6
|
||||||
|
d.frameHeader.YScale = b[6] >> 6
|
||||||
|
d.mbw = (d.frameHeader.Width + 0x0f) >> 4
|
||||||
|
d.mbh = (d.frameHeader.Height + 0x0f) >> 4
|
||||||
|
d.segmentHeader = segmentHeader{
|
||||||
|
prob: [3]uint8{0xff, 0xff, 0xff},
|
||||||
|
}
|
||||||
|
d.tokenProb = defaultTokenProb
|
||||||
|
d.segment = 0
|
||||||
|
return d.frameHeader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureImg ensures that d.img is large enough to hold the decoded frame.
|
||||||
|
func (d *Decoder) ensureImg() {
|
||||||
|
if d.img != nil {
|
||||||
|
p0, p1 := d.img.Rect.Min, d.img.Rect.Max
|
||||||
|
if p0.X == 0 && p0.Y == 0 && p1.X >= 16*d.mbw && p1.Y >= 16*d.mbh {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := image.NewYCbCr(image.Rect(0, 0, 16*d.mbw, 16*d.mbh), image.YCbCrSubsampleRatio420)
|
||||||
|
d.img = m.SubImage(image.Rect(0, 0, d.frameHeader.Width, d.frameHeader.Height)).(*image.YCbCr)
|
||||||
|
d.perMBFilterParams = make([]filterParam, d.mbw*d.mbh)
|
||||||
|
d.upMB = make([]mb, d.mbw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSegmentHeader parses the segment header, as specified in section 9.3.
|
||||||
|
func (d *Decoder) parseSegmentHeader() {
|
||||||
|
d.segmentHeader.useSegment = d.fp.readBit(uniformProb)
|
||||||
|
if !d.segmentHeader.useSegment {
|
||||||
|
d.segmentHeader.updateMap = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.segmentHeader.updateMap = d.fp.readBit(uniformProb)
|
||||||
|
if d.fp.readBit(uniformProb) {
|
||||||
|
d.segmentHeader.relativeDelta = !d.fp.readBit(uniformProb)
|
||||||
|
for i := range d.segmentHeader.quantizer {
|
||||||
|
d.segmentHeader.quantizer[i] = int8(d.fp.readOptionalInt(uniformProb, 7))
|
||||||
|
}
|
||||||
|
for i := range d.segmentHeader.filterStrength {
|
||||||
|
d.segmentHeader.filterStrength[i] = int8(d.fp.readOptionalInt(uniformProb, 6))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !d.segmentHeader.updateMap {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range d.segmentHeader.prob {
|
||||||
|
if d.fp.readBit(uniformProb) {
|
||||||
|
d.segmentHeader.prob[i] = uint8(d.fp.readUint(uniformProb, 8))
|
||||||
|
} else {
|
||||||
|
d.segmentHeader.prob[i] = 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFilterHeader parses the filter header, as specified in section 9.4.
|
||||||
|
func (d *Decoder) parseFilterHeader() {
|
||||||
|
d.filterHeader.simple = d.fp.readBit(uniformProb)
|
||||||
|
d.filterHeader.level = int8(d.fp.readUint(uniformProb, 6))
|
||||||
|
d.filterHeader.sharpness = uint8(d.fp.readUint(uniformProb, 3))
|
||||||
|
d.filterHeader.useLFDelta = d.fp.readBit(uniformProb)
|
||||||
|
if d.filterHeader.useLFDelta && d.fp.readBit(uniformProb) {
|
||||||
|
for i := range d.filterHeader.refLFDelta {
|
||||||
|
d.filterHeader.refLFDelta[i] = int8(d.fp.readOptionalInt(uniformProb, 6))
|
||||||
|
}
|
||||||
|
for i := range d.filterHeader.modeLFDelta {
|
||||||
|
d.filterHeader.modeLFDelta[i] = int8(d.fp.readOptionalInt(uniformProb, 6))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.filterHeader.level == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.segmentHeader.useSegment {
|
||||||
|
for i := range d.filterHeader.perSegmentLevel {
|
||||||
|
strength := d.segmentHeader.filterStrength[i]
|
||||||
|
if d.segmentHeader.relativeDelta {
|
||||||
|
strength += d.filterHeader.level
|
||||||
|
}
|
||||||
|
d.filterHeader.perSegmentLevel[i] = strength
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.filterHeader.perSegmentLevel[0] = d.filterHeader.level
|
||||||
|
}
|
||||||
|
d.computeFilterParams()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOtherPartitions parses the other partitions, as specified in section 9.5.
|
||||||
|
func (d *Decoder) parseOtherPartitions() error {
|
||||||
|
const maxNOP = 1 << 3
|
||||||
|
var partLens [maxNOP]int
|
||||||
|
d.nOP = 1 << d.fp.readUint(uniformProb, 2)
|
||||||
|
|
||||||
|
// The final partition length is implied by the the remaining chunk data
|
||||||
|
// (d.r.n) and the other d.nOP-1 partition lengths. Those d.nOP-1 partition
|
||||||
|
// lengths are stored as 24-bit uints, i.e. up to 16 MiB per partition.
|
||||||
|
n := 3 * (d.nOP - 1)
|
||||||
|
partLens[d.nOP-1] = d.r.n - n
|
||||||
|
if partLens[d.nOP-1] < 0 {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
buf := make([]byte, n)
|
||||||
|
if err := d.r.ReadFull(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < d.nOP-1; i++ {
|
||||||
|
pl := int(buf[3*i+0]) | int(buf[3*i+1])<<8 | int(buf[3*i+2])<<16
|
||||||
|
if pl > partLens[d.nOP-1] {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
partLens[i] = pl
|
||||||
|
partLens[d.nOP-1] -= pl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check if the final partition length can also fit into a 24-bit uint.
|
||||||
|
// Strictly speaking, this isn't part of the spec, but it guards against a
|
||||||
|
// malicious WEBP image that is too large to ReadFull the encoded DCT
|
||||||
|
// coefficients into memory, whether that's because the actual WEBP file is
|
||||||
|
// too large, or whether its RIFF metadata lists too large a chunk.
|
||||||
|
if 1<<24 <= partLens[d.nOP-1] {
|
||||||
|
return errors.New("vp8: too much data to decode")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, d.r.n)
|
||||||
|
if err := d.r.ReadFull(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, pl := range partLens {
|
||||||
|
if i == d.nOP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.op[i].init(buf[:pl])
|
||||||
|
buf = buf[pl:]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOtherHeaders parses header information other than the frame header.
|
||||||
|
func (d *Decoder) parseOtherHeaders() error {
|
||||||
|
// Initialize and parse the first partition.
|
||||||
|
firstPartition := make([]byte, d.frameHeader.FirstPartitionLen)
|
||||||
|
if err := d.r.ReadFull(firstPartition); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.fp.init(firstPartition)
|
||||||
|
if d.frameHeader.KeyFrame {
|
||||||
|
// Read and ignore the color space and pixel clamp values. They are
|
||||||
|
// specified in section 9.2, but are unimplemented.
|
||||||
|
d.fp.readBit(uniformProb)
|
||||||
|
d.fp.readBit(uniformProb)
|
||||||
|
}
|
||||||
|
d.parseSegmentHeader()
|
||||||
|
d.parseFilterHeader()
|
||||||
|
if err := d.parseOtherPartitions(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.parseQuant()
|
||||||
|
if !d.frameHeader.KeyFrame {
|
||||||
|
// Golden and AltRef frames are specified in section 9.7.
|
||||||
|
// TODO(nigeltao): implement. Note that they are only used for video, not still images.
|
||||||
|
return errors.New("vp8: Golden / AltRef frames are not implemented")
|
||||||
|
}
|
||||||
|
// Read and ignore the refreshLastFrameBuffer bit, specified in section 9.8.
|
||||||
|
// It applies only to video, and not still images.
|
||||||
|
d.fp.readBit(uniformProb)
|
||||||
|
d.parseTokenProb()
|
||||||
|
d.useSkipProb = d.fp.readBit(uniformProb)
|
||||||
|
if d.useSkipProb {
|
||||||
|
d.skipProb = uint8(d.fp.readUint(uniformProb, 8))
|
||||||
|
}
|
||||||
|
if d.fp.unexpectedEOF {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFrame decodes the frame and returns it as an YCbCr image.
|
||||||
|
// The image's contents are valid up until the next call to Decoder.Init.
|
||||||
|
func (d *Decoder) DecodeFrame() (*image.YCbCr, error) {
|
||||||
|
d.ensureImg()
|
||||||
|
if err := d.parseOtherHeaders(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Reconstruct the rows.
|
||||||
|
for mbx := 0; mbx < d.mbw; mbx++ {
|
||||||
|
d.upMB[mbx] = mb{}
|
||||||
|
}
|
||||||
|
for mby := 0; mby < d.mbh; mby++ {
|
||||||
|
d.leftMB = mb{}
|
||||||
|
for mbx := 0; mbx < d.mbw; mbx++ {
|
||||||
|
skip := d.reconstruct(mbx, mby)
|
||||||
|
fs := d.filterParams[d.segment][btou(!d.usePredY16)]
|
||||||
|
fs.inner = fs.inner || !skip
|
||||||
|
d.perMBFilterParams[d.mbw*mby+mbx] = fs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.fp.unexpectedEOF {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
for i := 0; i < d.nOP; i++ {
|
||||||
|
if d.op[i].unexpectedEOF {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Apply the loop filter.
|
||||||
|
//
|
||||||
|
// Even if we are using per-segment levels, section 15 says that "loop
|
||||||
|
// filtering must be skipped entirely if loop_filter_level at either the
|
||||||
|
// frame header level or macroblock override level is 0".
|
||||||
|
if d.filterHeader.level != 0 {
|
||||||
|
if d.filterHeader.simple {
|
||||||
|
d.simpleFilter()
|
||||||
|
} else {
|
||||||
|
d.normalFilter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d.img, nil
|
||||||
|
}
|
||||||
273
vendor/golang.org/x/image/vp8/filter.go
generated
vendored
Normal file
273
vendor/golang.org/x/image/vp8/filter.go
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// filter2 modifies a 2-pixel wide or 2-pixel high band along an edge.
|
||||||
|
func filter2(pix []byte, level, index, iStep, jStep int) {
|
||||||
|
for n := 16; n > 0; n, index = n-1, index+iStep {
|
||||||
|
p1 := int(pix[index-2*jStep])
|
||||||
|
p0 := int(pix[index-1*jStep])
|
||||||
|
q0 := int(pix[index+0*jStep])
|
||||||
|
q1 := int(pix[index+1*jStep])
|
||||||
|
if abs(p0-q0)<<1+abs(p1-q1)>>1 > level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
a := 3*(q0-p0) + clamp127(p1-q1)
|
||||||
|
a1 := clamp15((a + 4) >> 3)
|
||||||
|
a2 := clamp15((a + 3) >> 3)
|
||||||
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
||||||
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter246 modifies a 2-, 4- or 6-pixel wide or high band along an edge.
|
||||||
|
func filter246(pix []byte, n, level, ilevel, hlevel, index, iStep, jStep int, fourNotSix bool) {
|
||||||
|
for ; n > 0; n, index = n-1, index+iStep {
|
||||||
|
p3 := int(pix[index-4*jStep])
|
||||||
|
p2 := int(pix[index-3*jStep])
|
||||||
|
p1 := int(pix[index-2*jStep])
|
||||||
|
p0 := int(pix[index-1*jStep])
|
||||||
|
q0 := int(pix[index+0*jStep])
|
||||||
|
q1 := int(pix[index+1*jStep])
|
||||||
|
q2 := int(pix[index+2*jStep])
|
||||||
|
q3 := int(pix[index+3*jStep])
|
||||||
|
if abs(p0-q0)<<1+abs(p1-q1)>>1 > level {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if abs(p3-p2) > ilevel ||
|
||||||
|
abs(p2-p1) > ilevel ||
|
||||||
|
abs(p1-p0) > ilevel ||
|
||||||
|
abs(q1-q0) > ilevel ||
|
||||||
|
abs(q2-q1) > ilevel ||
|
||||||
|
abs(q3-q2) > ilevel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if abs(p1-p0) > hlevel || abs(q1-q0) > hlevel {
|
||||||
|
// Filter 2 pixels.
|
||||||
|
a := 3*(q0-p0) + clamp127(p1-q1)
|
||||||
|
a1 := clamp15((a + 4) >> 3)
|
||||||
|
a2 := clamp15((a + 3) >> 3)
|
||||||
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
||||||
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
||||||
|
} else if fourNotSix {
|
||||||
|
// Filter 4 pixels.
|
||||||
|
a := 3 * (q0 - p0)
|
||||||
|
a1 := clamp15((a + 4) >> 3)
|
||||||
|
a2 := clamp15((a + 3) >> 3)
|
||||||
|
a3 := (a1 + 1) >> 1
|
||||||
|
pix[index-2*jStep] = clamp255(p1 + a3)
|
||||||
|
pix[index-1*jStep] = clamp255(p0 + a2)
|
||||||
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
||||||
|
pix[index+1*jStep] = clamp255(q1 - a3)
|
||||||
|
} else {
|
||||||
|
// Filter 6 pixels.
|
||||||
|
a := clamp127(3*(q0-p0) + clamp127(p1-q1))
|
||||||
|
a1 := (27*a + 63) >> 7
|
||||||
|
a2 := (18*a + 63) >> 7
|
||||||
|
a3 := (9*a + 63) >> 7
|
||||||
|
pix[index-3*jStep] = clamp255(p2 + a3)
|
||||||
|
pix[index-2*jStep] = clamp255(p1 + a2)
|
||||||
|
pix[index-1*jStep] = clamp255(p0 + a1)
|
||||||
|
pix[index+0*jStep] = clamp255(q0 - a1)
|
||||||
|
pix[index+1*jStep] = clamp255(q1 - a2)
|
||||||
|
pix[index+2*jStep] = clamp255(q2 - a3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleFilter implements the simple filter, as specified in section 15.2.
|
||||||
|
func (d *Decoder) simpleFilter() {
|
||||||
|
for mby := 0; mby < d.mbh; mby++ {
|
||||||
|
for mbx := 0; mbx < d.mbw; mbx++ {
|
||||||
|
f := d.perMBFilterParams[d.mbw*mby+mbx]
|
||||||
|
if f.level == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l := int(f.level)
|
||||||
|
yIndex := (mby*d.img.YStride + mbx) * 16
|
||||||
|
if mbx > 0 {
|
||||||
|
filter2(d.img.Y, l+4, yIndex, d.img.YStride, 1)
|
||||||
|
}
|
||||||
|
if f.inner {
|
||||||
|
filter2(d.img.Y, l, yIndex+0x4, d.img.YStride, 1)
|
||||||
|
filter2(d.img.Y, l, yIndex+0x8, d.img.YStride, 1)
|
||||||
|
filter2(d.img.Y, l, yIndex+0xc, d.img.YStride, 1)
|
||||||
|
}
|
||||||
|
if mby > 0 {
|
||||||
|
filter2(d.img.Y, l+4, yIndex, 1, d.img.YStride)
|
||||||
|
}
|
||||||
|
if f.inner {
|
||||||
|
filter2(d.img.Y, l, yIndex+d.img.YStride*0x4, 1, d.img.YStride)
|
||||||
|
filter2(d.img.Y, l, yIndex+d.img.YStride*0x8, 1, d.img.YStride)
|
||||||
|
filter2(d.img.Y, l, yIndex+d.img.YStride*0xc, 1, d.img.YStride)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalFilter implements the normal filter, as specified in section 15.3.
|
||||||
|
func (d *Decoder) normalFilter() {
|
||||||
|
for mby := 0; mby < d.mbh; mby++ {
|
||||||
|
for mbx := 0; mbx < d.mbw; mbx++ {
|
||||||
|
f := d.perMBFilterParams[d.mbw*mby+mbx]
|
||||||
|
if f.level == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l, il, hl := int(f.level), int(f.ilevel), int(f.hlevel)
|
||||||
|
yIndex := (mby*d.img.YStride + mbx) * 16
|
||||||
|
cIndex := (mby*d.img.CStride + mbx) * 8
|
||||||
|
if mbx > 0 {
|
||||||
|
filter246(d.img.Y, 16, l+4, il, hl, yIndex, d.img.YStride, 1, false)
|
||||||
|
filter246(d.img.Cb, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false)
|
||||||
|
filter246(d.img.Cr, 8, l+4, il, hl, cIndex, d.img.CStride, 1, false)
|
||||||
|
}
|
||||||
|
if f.inner {
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+0x4, d.img.YStride, 1, true)
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+0x8, d.img.YStride, 1, true)
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+0xc, d.img.YStride, 1, true)
|
||||||
|
filter246(d.img.Cb, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true)
|
||||||
|
filter246(d.img.Cr, 8, l, il, hl, cIndex+0x4, d.img.CStride, 1, true)
|
||||||
|
}
|
||||||
|
if mby > 0 {
|
||||||
|
filter246(d.img.Y, 16, l+4, il, hl, yIndex, 1, d.img.YStride, false)
|
||||||
|
filter246(d.img.Cb, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false)
|
||||||
|
filter246(d.img.Cr, 8, l+4, il, hl, cIndex, 1, d.img.CStride, false)
|
||||||
|
}
|
||||||
|
if f.inner {
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x4, 1, d.img.YStride, true)
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0x8, 1, d.img.YStride, true)
|
||||||
|
filter246(d.img.Y, 16, l, il, hl, yIndex+d.img.YStride*0xc, 1, d.img.YStride, true)
|
||||||
|
filter246(d.img.Cb, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true)
|
||||||
|
filter246(d.img.Cr, 8, l, il, hl, cIndex+d.img.CStride*0x4, 1, d.img.CStride, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterParam holds the loop filter parameters for a macroblock.
|
||||||
|
type filterParam struct {
|
||||||
|
// The first three fields are thresholds used by the loop filter to smooth
|
||||||
|
// over the edges and interior of a macroblock. level is used by both the
|
||||||
|
// simple and normal filters. The inner level and high edge variance level
|
||||||
|
// are only used by the normal filter.
|
||||||
|
level, ilevel, hlevel uint8
|
||||||
|
// inner is whether the inner loop filter cannot be optimized out as a
|
||||||
|
// no-op for this particular macroblock.
|
||||||
|
inner bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeFilterParams computes the loop filter parameters, as specified in
|
||||||
|
// section 15.4.
|
||||||
|
func (d *Decoder) computeFilterParams() {
|
||||||
|
for i := range d.filterParams {
|
||||||
|
baseLevel := d.filterHeader.level
|
||||||
|
if d.segmentHeader.useSegment {
|
||||||
|
baseLevel = d.segmentHeader.filterStrength[i]
|
||||||
|
if d.segmentHeader.relativeDelta {
|
||||||
|
baseLevel += d.filterHeader.level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range d.filterParams[i] {
|
||||||
|
p := &d.filterParams[i][j]
|
||||||
|
p.inner = j != 0
|
||||||
|
level := baseLevel
|
||||||
|
if d.filterHeader.useLFDelta {
|
||||||
|
// The libwebp C code has a "TODO: only CURRENT is handled for now."
|
||||||
|
level += d.filterHeader.refLFDelta[0]
|
||||||
|
if j != 0 {
|
||||||
|
level += d.filterHeader.modeLFDelta[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if level <= 0 {
|
||||||
|
p.level = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if level > 63 {
|
||||||
|
level = 63
|
||||||
|
}
|
||||||
|
ilevel := level
|
||||||
|
if d.filterHeader.sharpness > 0 {
|
||||||
|
if d.filterHeader.sharpness > 4 {
|
||||||
|
ilevel >>= 2
|
||||||
|
} else {
|
||||||
|
ilevel >>= 1
|
||||||
|
}
|
||||||
|
if x := int8(9 - d.filterHeader.sharpness); ilevel > x {
|
||||||
|
ilevel = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ilevel < 1 {
|
||||||
|
ilevel = 1
|
||||||
|
}
|
||||||
|
p.ilevel = uint8(ilevel)
|
||||||
|
p.level = uint8(2*level + ilevel)
|
||||||
|
if d.frameHeader.KeyFrame {
|
||||||
|
if level < 15 {
|
||||||
|
p.hlevel = 0
|
||||||
|
} else if level < 40 {
|
||||||
|
p.hlevel = 1
|
||||||
|
} else {
|
||||||
|
p.hlevel = 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if level < 15 {
|
||||||
|
p.hlevel = 0
|
||||||
|
} else if level < 20 {
|
||||||
|
p.hlevel = 1
|
||||||
|
} else if level < 40 {
|
||||||
|
p.hlevel = 2
|
||||||
|
} else {
|
||||||
|
p.hlevel = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// intSize is either 32 or 64.
|
||||||
|
const intSize = 32 << (^uint(0) >> 63)
|
||||||
|
|
||||||
|
func abs(x int) int {
|
||||||
|
// m := -1 if x < 0. m := 0 otherwise.
|
||||||
|
m := x >> (intSize - 1)
|
||||||
|
|
||||||
|
// In two's complement representation, the negative number
|
||||||
|
// of any number (except the smallest one) can be computed
|
||||||
|
// by flipping all the bits and add 1. This is faster than
|
||||||
|
// code with a branch.
|
||||||
|
// See Hacker's Delight, section 2-4.
|
||||||
|
return (x ^ m) - m
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp15(x int) int {
|
||||||
|
if x < -16 {
|
||||||
|
return -16
|
||||||
|
}
|
||||||
|
if x > 15 {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp127(x int) int {
|
||||||
|
if x < -128 {
|
||||||
|
return -128
|
||||||
|
}
|
||||||
|
if x > 127 {
|
||||||
|
return 127
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp255(x int) uint8 {
|
||||||
|
if x < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if x > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(x)
|
||||||
|
}
|
||||||
98
vendor/golang.org/x/image/vp8/idct.go
generated
vendored
Normal file
98
vendor/golang.org/x/image/vp8/idct.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file implements the inverse Discrete Cosine Transform and the inverse
|
||||||
|
// Walsh Hadamard Transform (WHT), as specified in sections 14.3 and 14.4.
|
||||||
|
|
||||||
|
func clip8(i int32) uint8 {
|
||||||
|
if i < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if i > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Decoder) inverseDCT4(y, x, coeffBase int) {
|
||||||
|
const (
|
||||||
|
c1 = 85627 // 65536 * cos(pi/8) * sqrt(2).
|
||||||
|
c2 = 35468 // 65536 * sin(pi/8) * sqrt(2).
|
||||||
|
)
|
||||||
|
var m [4][4]int32
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
a := int32(z.coeff[coeffBase+0]) + int32(z.coeff[coeffBase+8])
|
||||||
|
b := int32(z.coeff[coeffBase+0]) - int32(z.coeff[coeffBase+8])
|
||||||
|
c := (int32(z.coeff[coeffBase+4])*c2)>>16 - (int32(z.coeff[coeffBase+12])*c1)>>16
|
||||||
|
d := (int32(z.coeff[coeffBase+4])*c1)>>16 + (int32(z.coeff[coeffBase+12])*c2)>>16
|
||||||
|
m[i][0] = a + d
|
||||||
|
m[i][1] = b + c
|
||||||
|
m[i][2] = b - c
|
||||||
|
m[i][3] = a - d
|
||||||
|
coeffBase++
|
||||||
|
}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
dc := m[0][j] + 4
|
||||||
|
a := dc + m[2][j]
|
||||||
|
b := dc - m[2][j]
|
||||||
|
c := (m[1][j]*c2)>>16 - (m[3][j]*c1)>>16
|
||||||
|
d := (m[1][j]*c1)>>16 + (m[3][j]*c2)>>16
|
||||||
|
z.ybr[y+j][x+0] = clip8(int32(z.ybr[y+j][x+0]) + (a+d)>>3)
|
||||||
|
z.ybr[y+j][x+1] = clip8(int32(z.ybr[y+j][x+1]) + (b+c)>>3)
|
||||||
|
z.ybr[y+j][x+2] = clip8(int32(z.ybr[y+j][x+2]) + (b-c)>>3)
|
||||||
|
z.ybr[y+j][x+3] = clip8(int32(z.ybr[y+j][x+3]) + (a-d)>>3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Decoder) inverseDCT4DCOnly(y, x, coeffBase int) {
|
||||||
|
dc := (int32(z.coeff[coeffBase+0]) + 4) >> 3
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
z.ybr[y+j][x+i] = clip8(int32(z.ybr[y+j][x+i]) + dc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Decoder) inverseDCT8(y, x, coeffBase int) {
|
||||||
|
z.inverseDCT4(y+0, x+0, coeffBase+0*16)
|
||||||
|
z.inverseDCT4(y+0, x+4, coeffBase+1*16)
|
||||||
|
z.inverseDCT4(y+4, x+0, coeffBase+2*16)
|
||||||
|
z.inverseDCT4(y+4, x+4, coeffBase+3*16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Decoder) inverseDCT8DCOnly(y, x, coeffBase int) {
|
||||||
|
z.inverseDCT4DCOnly(y+0, x+0, coeffBase+0*16)
|
||||||
|
z.inverseDCT4DCOnly(y+0, x+4, coeffBase+1*16)
|
||||||
|
z.inverseDCT4DCOnly(y+4, x+0, coeffBase+2*16)
|
||||||
|
z.inverseDCT4DCOnly(y+4, x+4, coeffBase+3*16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) inverseWHT16() {
|
||||||
|
var m [16]int32
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
a0 := int32(d.coeff[384+0+i]) + int32(d.coeff[384+12+i])
|
||||||
|
a1 := int32(d.coeff[384+4+i]) + int32(d.coeff[384+8+i])
|
||||||
|
a2 := int32(d.coeff[384+4+i]) - int32(d.coeff[384+8+i])
|
||||||
|
a3 := int32(d.coeff[384+0+i]) - int32(d.coeff[384+12+i])
|
||||||
|
m[0+i] = a0 + a1
|
||||||
|
m[8+i] = a0 - a1
|
||||||
|
m[4+i] = a3 + a2
|
||||||
|
m[12+i] = a3 - a2
|
||||||
|
}
|
||||||
|
out := 0
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
dc := m[0+i*4] + 3
|
||||||
|
a0 := dc + m[3+i*4]
|
||||||
|
a1 := m[1+i*4] + m[2+i*4]
|
||||||
|
a2 := m[1+i*4] - m[2+i*4]
|
||||||
|
a3 := dc - m[3+i*4]
|
||||||
|
d.coeff[out+0] = int16((a0 + a1) >> 3)
|
||||||
|
d.coeff[out+16] = int16((a3 + a2) >> 3)
|
||||||
|
d.coeff[out+32] = int16((a0 - a1) >> 3)
|
||||||
|
d.coeff[out+48] = int16((a3 - a2) >> 3)
|
||||||
|
out += 64
|
||||||
|
}
|
||||||
|
}
|
||||||
129
vendor/golang.org/x/image/vp8/partition.go
generated
vendored
Normal file
129
vendor/golang.org/x/image/vp8/partition.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// Each VP8 frame consists of between 2 and 9 bitstream partitions.
|
||||||
|
// Each partition is byte-aligned and is independently arithmetic-encoded.
|
||||||
|
//
|
||||||
|
// This file implements decoding a partition's bitstream, as specified in
|
||||||
|
// chapter 7. The implementation follows libwebp's approach instead of the
|
||||||
|
// specification's reference C implementation. For example, we use a look-up
|
||||||
|
// table instead of a for loop to recalibrate the encoded range.
|
||||||
|
|
||||||
|
var (
|
||||||
|
lutShift = [127]uint8{
|
||||||
|
7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
}
|
||||||
|
lutRangeM1 = [127]uint8{
|
||||||
|
127,
|
||||||
|
127, 191,
|
||||||
|
127, 159, 191, 223,
|
||||||
|
127, 143, 159, 175, 191, 207, 223, 239,
|
||||||
|
127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247,
|
||||||
|
127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187,
|
||||||
|
191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251,
|
||||||
|
127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157,
|
||||||
|
159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189,
|
||||||
|
191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221,
|
||||||
|
223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// uniformProb represents a 50% probability that the next bit is 0.
|
||||||
|
const uniformProb = 128
|
||||||
|
|
||||||
|
// partition holds arithmetic-coded bits.
|
||||||
|
type partition struct {
|
||||||
|
// buf is the input bytes.
|
||||||
|
buf []byte
|
||||||
|
// r is how many of buf's bytes have been consumed.
|
||||||
|
r int
|
||||||
|
// rangeM1 is range minus 1, where range is in the arithmetic coding sense,
|
||||||
|
// not the Go language sense.
|
||||||
|
rangeM1 uint32
|
||||||
|
// bits and nBits hold those bits shifted out of buf but not yet consumed.
|
||||||
|
bits uint32
|
||||||
|
nBits uint8
|
||||||
|
// unexpectedEOF tells whether we tried to read past buf.
|
||||||
|
unexpectedEOF bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes the partition.
|
||||||
|
func (p *partition) init(buf []byte) {
|
||||||
|
p.buf = buf
|
||||||
|
p.r = 0
|
||||||
|
p.rangeM1 = 254
|
||||||
|
p.bits = 0
|
||||||
|
p.nBits = 0
|
||||||
|
p.unexpectedEOF = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// readBit returns the next bit.
|
||||||
|
func (p *partition) readBit(prob uint8) bool {
|
||||||
|
if p.nBits < 8 {
|
||||||
|
if p.r >= len(p.buf) {
|
||||||
|
p.unexpectedEOF = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Expression split for 386 compiler.
|
||||||
|
x := uint32(p.buf[p.r])
|
||||||
|
p.bits |= x << (8 - p.nBits)
|
||||||
|
p.r++
|
||||||
|
p.nBits += 8
|
||||||
|
}
|
||||||
|
split := (p.rangeM1*uint32(prob))>>8 + 1
|
||||||
|
bit := p.bits >= split<<8
|
||||||
|
if bit {
|
||||||
|
p.rangeM1 -= split
|
||||||
|
p.bits -= split << 8
|
||||||
|
} else {
|
||||||
|
p.rangeM1 = split - 1
|
||||||
|
}
|
||||||
|
if p.rangeM1 < 127 {
|
||||||
|
shift := lutShift[p.rangeM1]
|
||||||
|
p.rangeM1 = uint32(lutRangeM1[p.rangeM1])
|
||||||
|
p.bits <<= shift
|
||||||
|
p.nBits -= shift
|
||||||
|
}
|
||||||
|
return bit
|
||||||
|
}
|
||||||
|
|
||||||
|
// readUint returns the next n-bit unsigned integer.
|
||||||
|
func (p *partition) readUint(prob, n uint8) uint32 {
|
||||||
|
var u uint32
|
||||||
|
for n > 0 {
|
||||||
|
n--
|
||||||
|
if p.readBit(prob) {
|
||||||
|
u |= 1 << n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// readInt returns the next n-bit signed integer.
|
||||||
|
func (p *partition) readInt(prob, n uint8) int32 {
|
||||||
|
u := p.readUint(prob, n)
|
||||||
|
b := p.readBit(prob)
|
||||||
|
if b {
|
||||||
|
return -int32(u)
|
||||||
|
}
|
||||||
|
return int32(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readOptionalInt returns the next n-bit signed integer in an encoding
|
||||||
|
// where the likely result is zero.
|
||||||
|
func (p *partition) readOptionalInt(prob, n uint8) int32 {
|
||||||
|
if !p.readBit(prob) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return p.readInt(prob, n)
|
||||||
|
}
|
||||||
201
vendor/golang.org/x/image/vp8/pred.go
generated
vendored
Normal file
201
vendor/golang.org/x/image/vp8/pred.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file implements parsing the predictor modes, as specified in chapter
|
||||||
|
// 11.
|
||||||
|
|
||||||
|
func (d *Decoder) parsePredModeY16(mbx int) {
|
||||||
|
var p uint8
|
||||||
|
if !d.fp.readBit(156) {
|
||||||
|
if !d.fp.readBit(163) {
|
||||||
|
p = predDC
|
||||||
|
} else {
|
||||||
|
p = predVE
|
||||||
|
}
|
||||||
|
} else if !d.fp.readBit(128) {
|
||||||
|
p = predHE
|
||||||
|
} else {
|
||||||
|
p = predTM
|
||||||
|
}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
d.upMB[mbx].pred[i] = p
|
||||||
|
d.leftMB.pred[i] = p
|
||||||
|
}
|
||||||
|
d.predY16 = p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) parsePredModeC8() {
|
||||||
|
if !d.fp.readBit(142) {
|
||||||
|
d.predC8 = predDC
|
||||||
|
} else if !d.fp.readBit(114) {
|
||||||
|
d.predC8 = predVE
|
||||||
|
} else if !d.fp.readBit(183) {
|
||||||
|
d.predC8 = predHE
|
||||||
|
} else {
|
||||||
|
d.predC8 = predTM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) parsePredModeY4(mbx int) {
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
p := d.leftMB.pred[j]
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
prob := &predProb[d.upMB[mbx].pred[i]][p]
|
||||||
|
if !d.fp.readBit(prob[0]) {
|
||||||
|
p = predDC
|
||||||
|
} else if !d.fp.readBit(prob[1]) {
|
||||||
|
p = predTM
|
||||||
|
} else if !d.fp.readBit(prob[2]) {
|
||||||
|
p = predVE
|
||||||
|
} else if !d.fp.readBit(prob[3]) {
|
||||||
|
if !d.fp.readBit(prob[4]) {
|
||||||
|
p = predHE
|
||||||
|
} else if !d.fp.readBit(prob[5]) {
|
||||||
|
p = predRD
|
||||||
|
} else {
|
||||||
|
p = predVR
|
||||||
|
}
|
||||||
|
} else if !d.fp.readBit(prob[6]) {
|
||||||
|
p = predLD
|
||||||
|
} else if !d.fp.readBit(prob[7]) {
|
||||||
|
p = predVL
|
||||||
|
} else if !d.fp.readBit(prob[8]) {
|
||||||
|
p = predHD
|
||||||
|
} else {
|
||||||
|
p = predHU
|
||||||
|
}
|
||||||
|
d.predY4[j][i] = p
|
||||||
|
d.upMB[mbx].pred[i] = p
|
||||||
|
}
|
||||||
|
d.leftMB.pred[j] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// predProb are the probabilities to decode a 4x4 region's predictor mode given
|
||||||
|
// the predictor modes of the regions above and left of it.
|
||||||
|
// These values are specified in section 11.5.
|
||||||
|
var predProb = [nPred][nPred][9]uint8{
|
||||||
|
{
|
||||||
|
{231, 120, 48, 89, 115, 113, 120, 152, 112},
|
||||||
|
{152, 179, 64, 126, 170, 118, 46, 70, 95},
|
||||||
|
{175, 69, 143, 80, 85, 82, 72, 155, 103},
|
||||||
|
{56, 58, 10, 171, 218, 189, 17, 13, 152},
|
||||||
|
{114, 26, 17, 163, 44, 195, 21, 10, 173},
|
||||||
|
{121, 24, 80, 195, 26, 62, 44, 64, 85},
|
||||||
|
{144, 71, 10, 38, 171, 213, 144, 34, 26},
|
||||||
|
{170, 46, 55, 19, 136, 160, 33, 206, 71},
|
||||||
|
{63, 20, 8, 114, 114, 208, 12, 9, 226},
|
||||||
|
{81, 40, 11, 96, 182, 84, 29, 16, 36},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{134, 183, 89, 137, 98, 101, 106, 165, 148},
|
||||||
|
{72, 187, 100, 130, 157, 111, 32, 75, 80},
|
||||||
|
{66, 102, 167, 99, 74, 62, 40, 234, 128},
|
||||||
|
{41, 53, 9, 178, 241, 141, 26, 8, 107},
|
||||||
|
{74, 43, 26, 146, 73, 166, 49, 23, 157},
|
||||||
|
{65, 38, 105, 160, 51, 52, 31, 115, 128},
|
||||||
|
{104, 79, 12, 27, 217, 255, 87, 17, 7},
|
||||||
|
{87, 68, 71, 44, 114, 51, 15, 186, 23},
|
||||||
|
{47, 41, 14, 110, 182, 183, 21, 17, 194},
|
||||||
|
{66, 45, 25, 102, 197, 189, 23, 18, 22},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{88, 88, 147, 150, 42, 46, 45, 196, 205},
|
||||||
|
{43, 97, 183, 117, 85, 38, 35, 179, 61},
|
||||||
|
{39, 53, 200, 87, 26, 21, 43, 232, 171},
|
||||||
|
{56, 34, 51, 104, 114, 102, 29, 93, 77},
|
||||||
|
{39, 28, 85, 171, 58, 165, 90, 98, 64},
|
||||||
|
{34, 22, 116, 206, 23, 34, 43, 166, 73},
|
||||||
|
{107, 54, 32, 26, 51, 1, 81, 43, 31},
|
||||||
|
{68, 25, 106, 22, 64, 171, 36, 225, 114},
|
||||||
|
{34, 19, 21, 102, 132, 188, 16, 76, 124},
|
||||||
|
{62, 18, 78, 95, 85, 57, 50, 48, 51},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{193, 101, 35, 159, 215, 111, 89, 46, 111},
|
||||||
|
{60, 148, 31, 172, 219, 228, 21, 18, 111},
|
||||||
|
{112, 113, 77, 85, 179, 255, 38, 120, 114},
|
||||||
|
{40, 42, 1, 196, 245, 209, 10, 25, 109},
|
||||||
|
{88, 43, 29, 140, 166, 213, 37, 43, 154},
|
||||||
|
{61, 63, 30, 155, 67, 45, 68, 1, 209},
|
||||||
|
{100, 80, 8, 43, 154, 1, 51, 26, 71},
|
||||||
|
{142, 78, 78, 16, 255, 128, 34, 197, 171},
|
||||||
|
{41, 40, 5, 102, 211, 183, 4, 1, 221},
|
||||||
|
{51, 50, 17, 168, 209, 192, 23, 25, 82},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{138, 31, 36, 171, 27, 166, 38, 44, 229},
|
||||||
|
{67, 87, 58, 169, 82, 115, 26, 59, 179},
|
||||||
|
{63, 59, 90, 180, 59, 166, 93, 73, 154},
|
||||||
|
{40, 40, 21, 116, 143, 209, 34, 39, 175},
|
||||||
|
{47, 15, 16, 183, 34, 223, 49, 45, 183},
|
||||||
|
{46, 17, 33, 183, 6, 98, 15, 32, 183},
|
||||||
|
{57, 46, 22, 24, 128, 1, 54, 17, 37},
|
||||||
|
{65, 32, 73, 115, 28, 128, 23, 128, 205},
|
||||||
|
{40, 3, 9, 115, 51, 192, 18, 6, 223},
|
||||||
|
{87, 37, 9, 115, 59, 77, 64, 21, 47},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{104, 55, 44, 218, 9, 54, 53, 130, 226},
|
||||||
|
{64, 90, 70, 205, 40, 41, 23, 26, 57},
|
||||||
|
{54, 57, 112, 184, 5, 41, 38, 166, 213},
|
||||||
|
{30, 34, 26, 133, 152, 116, 10, 32, 134},
|
||||||
|
{39, 19, 53, 221, 26, 114, 32, 73, 255},
|
||||||
|
{31, 9, 65, 234, 2, 15, 1, 118, 73},
|
||||||
|
{75, 32, 12, 51, 192, 255, 160, 43, 51},
|
||||||
|
{88, 31, 35, 67, 102, 85, 55, 186, 85},
|
||||||
|
{56, 21, 23, 111, 59, 205, 45, 37, 192},
|
||||||
|
{55, 38, 70, 124, 73, 102, 1, 34, 98},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{125, 98, 42, 88, 104, 85, 117, 175, 82},
|
||||||
|
{95, 84, 53, 89, 128, 100, 113, 101, 45},
|
||||||
|
{75, 79, 123, 47, 51, 128, 81, 171, 1},
|
||||||
|
{57, 17, 5, 71, 102, 57, 53, 41, 49},
|
||||||
|
{38, 33, 13, 121, 57, 73, 26, 1, 85},
|
||||||
|
{41, 10, 67, 138, 77, 110, 90, 47, 114},
|
||||||
|
{115, 21, 2, 10, 102, 255, 166, 23, 6},
|
||||||
|
{101, 29, 16, 10, 85, 128, 101, 196, 26},
|
||||||
|
{57, 18, 10, 102, 102, 213, 34, 20, 43},
|
||||||
|
{117, 20, 15, 36, 163, 128, 68, 1, 26},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{102, 61, 71, 37, 34, 53, 31, 243, 192},
|
||||||
|
{69, 60, 71, 38, 73, 119, 28, 222, 37},
|
||||||
|
{68, 45, 128, 34, 1, 47, 11, 245, 171},
|
||||||
|
{62, 17, 19, 70, 146, 85, 55, 62, 70},
|
||||||
|
{37, 43, 37, 154, 100, 163, 85, 160, 1},
|
||||||
|
{63, 9, 92, 136, 28, 64, 32, 201, 85},
|
||||||
|
{75, 15, 9, 9, 64, 255, 184, 119, 16},
|
||||||
|
{86, 6, 28, 5, 64, 255, 25, 248, 1},
|
||||||
|
{56, 8, 17, 132, 137, 255, 55, 116, 128},
|
||||||
|
{58, 15, 20, 82, 135, 57, 26, 121, 40},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{164, 50, 31, 137, 154, 133, 25, 35, 218},
|
||||||
|
{51, 103, 44, 131, 131, 123, 31, 6, 158},
|
||||||
|
{86, 40, 64, 135, 148, 224, 45, 183, 128},
|
||||||
|
{22, 26, 17, 131, 240, 154, 14, 1, 209},
|
||||||
|
{45, 16, 21, 91, 64, 222, 7, 1, 197},
|
||||||
|
{56, 21, 39, 155, 60, 138, 23, 102, 213},
|
||||||
|
{83, 12, 13, 54, 192, 255, 68, 47, 28},
|
||||||
|
{85, 26, 85, 85, 128, 128, 32, 146, 171},
|
||||||
|
{18, 11, 7, 63, 144, 171, 4, 4, 246},
|
||||||
|
{35, 27, 10, 146, 174, 171, 12, 26, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{190, 80, 35, 99, 180, 80, 126, 54, 45},
|
||||||
|
{85, 126, 47, 87, 176, 51, 41, 20, 32},
|
||||||
|
{101, 75, 128, 139, 118, 146, 116, 128, 85},
|
||||||
|
{56, 41, 15, 176, 236, 85, 37, 9, 62},
|
||||||
|
{71, 30, 17, 119, 118, 255, 17, 18, 138},
|
||||||
|
{101, 38, 60, 138, 55, 70, 43, 26, 142},
|
||||||
|
{146, 36, 19, 30, 171, 255, 97, 27, 20},
|
||||||
|
{138, 45, 61, 62, 219, 1, 81, 188, 64},
|
||||||
|
{32, 41, 20, 117, 151, 142, 20, 21, 163},
|
||||||
|
{112, 19, 12, 61, 195, 128, 48, 4, 24},
|
||||||
|
},
|
||||||
|
}
|
||||||
553
vendor/golang.org/x/image/vp8/predfunc.go
generated
vendored
Normal file
553
vendor/golang.org/x/image/vp8/predfunc.go
generated
vendored
Normal file
@@ -0,0 +1,553 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file implements the predicition functions, as specified in chapter 12.
|
||||||
|
//
|
||||||
|
// For each macroblock (of 1x16x16 luma and 2x8x8 chroma coefficients), the
|
||||||
|
// luma values are either predicted as one large 16x16 region or 16 separate
|
||||||
|
// 4x4 regions. The chroma values are always predicted as one 8x8 region.
|
||||||
|
//
|
||||||
|
// For 4x4 regions, the target block's predicted values (Xs) are a function of
|
||||||
|
// its previously-decoded top and left border values, as well as a number of
|
||||||
|
// pixels from the top-right:
|
||||||
|
//
|
||||||
|
// a b c d e f g h
|
||||||
|
// p X X X X
|
||||||
|
// q X X X X
|
||||||
|
// r X X X X
|
||||||
|
// s X X X X
|
||||||
|
//
|
||||||
|
// The predictor modes are:
|
||||||
|
// - DC: all Xs = (b + c + d + e + p + q + r + s + 4) / 8.
|
||||||
|
// - TM: the first X = (b + p - a), the second X = (c + p - a), and so on.
|
||||||
|
// - VE: each X = the weighted average of its column's top value and that
|
||||||
|
// value's neighbors, i.e. averages of abc, bcd, cde or def.
|
||||||
|
// - HE: similar to VE except rows instead of columns, and the final row is
|
||||||
|
// an average of r, s and s.
|
||||||
|
// - RD, VR, LD, VL, HD, HU: these diagonal modes ("Right Down", "Vertical
|
||||||
|
// Right", etc) are more complicated and are described in section 12.3.
|
||||||
|
// All Xs are clipped to the range [0, 255].
|
||||||
|
//
|
||||||
|
// For 8x8 and 16x16 regions, the target block's predicted values are a
|
||||||
|
// function of the top and left border values without the top-right overhang,
|
||||||
|
// i.e. without the 8x8 or 16x16 equivalent of f, g and h. Furthermore:
|
||||||
|
// - There are no diagonal predictor modes, only DC, TM, VE and HE.
|
||||||
|
// - The DC mode has variants for macroblocks in the top row and/or left
|
||||||
|
// column, i.e. for macroblocks with mby == 0 || mbx == 0.
|
||||||
|
// - The VE and HE modes take only the column top or row left values; they do
|
||||||
|
// not smooth that top/left value with its neighbors.
|
||||||
|
|
||||||
|
// nPred is the number of predictor modes, not including the Top/Left versions
|
||||||
|
// of the DC predictor mode.
|
||||||
|
const nPred = 10
|
||||||
|
|
||||||
|
const (
|
||||||
|
predDC = iota
|
||||||
|
predTM
|
||||||
|
predVE
|
||||||
|
predHE
|
||||||
|
predRD
|
||||||
|
predVR
|
||||||
|
predLD
|
||||||
|
predVL
|
||||||
|
predHD
|
||||||
|
predHU
|
||||||
|
predDCTop
|
||||||
|
predDCLeft
|
||||||
|
predDCTopLeft
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkTopLeftPred(mbx, mby int, p uint8) uint8 {
|
||||||
|
if p != predDC {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
if mbx == 0 {
|
||||||
|
if mby == 0 {
|
||||||
|
return predDCTopLeft
|
||||||
|
}
|
||||||
|
return predDCLeft
|
||||||
|
}
|
||||||
|
if mby == 0 {
|
||||||
|
return predDCTop
|
||||||
|
}
|
||||||
|
return predDC
|
||||||
|
}
|
||||||
|
|
||||||
|
var predFunc4 = [...]func(*Decoder, int, int){
|
||||||
|
predFunc4DC,
|
||||||
|
predFunc4TM,
|
||||||
|
predFunc4VE,
|
||||||
|
predFunc4HE,
|
||||||
|
predFunc4RD,
|
||||||
|
predFunc4VR,
|
||||||
|
predFunc4LD,
|
||||||
|
predFunc4VL,
|
||||||
|
predFunc4HD,
|
||||||
|
predFunc4HU,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
var predFunc8 = [...]func(*Decoder, int, int){
|
||||||
|
predFunc8DC,
|
||||||
|
predFunc8TM,
|
||||||
|
predFunc8VE,
|
||||||
|
predFunc8HE,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
predFunc8DCTop,
|
||||||
|
predFunc8DCLeft,
|
||||||
|
predFunc8DCTopLeft,
|
||||||
|
}
|
||||||
|
|
||||||
|
var predFunc16 = [...]func(*Decoder, int, int){
|
||||||
|
predFunc16DC,
|
||||||
|
predFunc16TM,
|
||||||
|
predFunc16VE,
|
||||||
|
predFunc16HE,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
predFunc16DCTop,
|
||||||
|
predFunc16DCLeft,
|
||||||
|
predFunc16DCTopLeft,
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4DC(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(4)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
sum += uint32(z.ybr[y-1][x+i])
|
||||||
|
}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
sum += uint32(z.ybr[y+j][x-1])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 8)
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4TM(z *Decoder, y, x int) {
|
||||||
|
delta0 := -int32(z.ybr[y-1][x-1])
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
delta1 := delta0 + int32(z.ybr[y+j][x-1])
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
delta2 := delta1 + int32(z.ybr[y-1][x+i])
|
||||||
|
z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4VE(z *Decoder, y, x int) {
|
||||||
|
a := int32(z.ybr[y-1][x-1])
|
||||||
|
b := int32(z.ybr[y-1][x+0])
|
||||||
|
c := int32(z.ybr[y-1][x+1])
|
||||||
|
d := int32(z.ybr[y-1][x+2])
|
||||||
|
e := int32(z.ybr[y-1][x+3])
|
||||||
|
f := int32(z.ybr[y-1][x+4])
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
cde := uint8((c + 2*d + e + 2) / 4)
|
||||||
|
def := uint8((d + 2*e + f + 2) / 4)
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
z.ybr[y+j][x+0] = abc
|
||||||
|
z.ybr[y+j][x+1] = bcd
|
||||||
|
z.ybr[y+j][x+2] = cde
|
||||||
|
z.ybr[y+j][x+3] = def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4HE(z *Decoder, y, x int) {
|
||||||
|
s := int32(z.ybr[y+3][x-1])
|
||||||
|
r := int32(z.ybr[y+2][x-1])
|
||||||
|
q := int32(z.ybr[y+1][x-1])
|
||||||
|
p := int32(z.ybr[y+0][x-1])
|
||||||
|
a := int32(z.ybr[y-1][x-1])
|
||||||
|
ssr := uint8((s + 2*s + r + 2) / 4)
|
||||||
|
srq := uint8((s + 2*r + q + 2) / 4)
|
||||||
|
rqp := uint8((r + 2*q + p + 2) / 4)
|
||||||
|
apq := uint8((a + 2*p + q + 2) / 4)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
z.ybr[y+0][x+i] = apq
|
||||||
|
z.ybr[y+1][x+i] = rqp
|
||||||
|
z.ybr[y+2][x+i] = srq
|
||||||
|
z.ybr[y+3][x+i] = ssr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4RD(z *Decoder, y, x int) {
|
||||||
|
s := int32(z.ybr[y+3][x-1])
|
||||||
|
r := int32(z.ybr[y+2][x-1])
|
||||||
|
q := int32(z.ybr[y+1][x-1])
|
||||||
|
p := int32(z.ybr[y+0][x-1])
|
||||||
|
a := int32(z.ybr[y-1][x-1])
|
||||||
|
b := int32(z.ybr[y-1][x+0])
|
||||||
|
c := int32(z.ybr[y-1][x+1])
|
||||||
|
d := int32(z.ybr[y-1][x+2])
|
||||||
|
e := int32(z.ybr[y-1][x+3])
|
||||||
|
srq := uint8((s + 2*r + q + 2) / 4)
|
||||||
|
rqp := uint8((r + 2*q + p + 2) / 4)
|
||||||
|
qpa := uint8((q + 2*p + a + 2) / 4)
|
||||||
|
pab := uint8((p + 2*a + b + 2) / 4)
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
cde := uint8((c + 2*d + e + 2) / 4)
|
||||||
|
z.ybr[y+0][x+0] = pab
|
||||||
|
z.ybr[y+0][x+1] = abc
|
||||||
|
z.ybr[y+0][x+2] = bcd
|
||||||
|
z.ybr[y+0][x+3] = cde
|
||||||
|
z.ybr[y+1][x+0] = qpa
|
||||||
|
z.ybr[y+1][x+1] = pab
|
||||||
|
z.ybr[y+1][x+2] = abc
|
||||||
|
z.ybr[y+1][x+3] = bcd
|
||||||
|
z.ybr[y+2][x+0] = rqp
|
||||||
|
z.ybr[y+2][x+1] = qpa
|
||||||
|
z.ybr[y+2][x+2] = pab
|
||||||
|
z.ybr[y+2][x+3] = abc
|
||||||
|
z.ybr[y+3][x+0] = srq
|
||||||
|
z.ybr[y+3][x+1] = rqp
|
||||||
|
z.ybr[y+3][x+2] = qpa
|
||||||
|
z.ybr[y+3][x+3] = pab
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4VR(z *Decoder, y, x int) {
|
||||||
|
r := int32(z.ybr[y+2][x-1])
|
||||||
|
q := int32(z.ybr[y+1][x-1])
|
||||||
|
p := int32(z.ybr[y+0][x-1])
|
||||||
|
a := int32(z.ybr[y-1][x-1])
|
||||||
|
b := int32(z.ybr[y-1][x+0])
|
||||||
|
c := int32(z.ybr[y-1][x+1])
|
||||||
|
d := int32(z.ybr[y-1][x+2])
|
||||||
|
e := int32(z.ybr[y-1][x+3])
|
||||||
|
ab := uint8((a + b + 1) / 2)
|
||||||
|
bc := uint8((b + c + 1) / 2)
|
||||||
|
cd := uint8((c + d + 1) / 2)
|
||||||
|
de := uint8((d + e + 1) / 2)
|
||||||
|
rqp := uint8((r + 2*q + p + 2) / 4)
|
||||||
|
qpa := uint8((q + 2*p + a + 2) / 4)
|
||||||
|
pab := uint8((p + 2*a + b + 2) / 4)
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
cde := uint8((c + 2*d + e + 2) / 4)
|
||||||
|
z.ybr[y+0][x+0] = ab
|
||||||
|
z.ybr[y+0][x+1] = bc
|
||||||
|
z.ybr[y+0][x+2] = cd
|
||||||
|
z.ybr[y+0][x+3] = de
|
||||||
|
z.ybr[y+1][x+0] = pab
|
||||||
|
z.ybr[y+1][x+1] = abc
|
||||||
|
z.ybr[y+1][x+2] = bcd
|
||||||
|
z.ybr[y+1][x+3] = cde
|
||||||
|
z.ybr[y+2][x+0] = qpa
|
||||||
|
z.ybr[y+2][x+1] = ab
|
||||||
|
z.ybr[y+2][x+2] = bc
|
||||||
|
z.ybr[y+2][x+3] = cd
|
||||||
|
z.ybr[y+3][x+0] = rqp
|
||||||
|
z.ybr[y+3][x+1] = pab
|
||||||
|
z.ybr[y+3][x+2] = abc
|
||||||
|
z.ybr[y+3][x+3] = bcd
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4LD(z *Decoder, y, x int) {
|
||||||
|
a := int32(z.ybr[y-1][x+0])
|
||||||
|
b := int32(z.ybr[y-1][x+1])
|
||||||
|
c := int32(z.ybr[y-1][x+2])
|
||||||
|
d := int32(z.ybr[y-1][x+3])
|
||||||
|
e := int32(z.ybr[y-1][x+4])
|
||||||
|
f := int32(z.ybr[y-1][x+5])
|
||||||
|
g := int32(z.ybr[y-1][x+6])
|
||||||
|
h := int32(z.ybr[y-1][x+7])
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
cde := uint8((c + 2*d + e + 2) / 4)
|
||||||
|
def := uint8((d + 2*e + f + 2) / 4)
|
||||||
|
efg := uint8((e + 2*f + g + 2) / 4)
|
||||||
|
fgh := uint8((f + 2*g + h + 2) / 4)
|
||||||
|
ghh := uint8((g + 2*h + h + 2) / 4)
|
||||||
|
z.ybr[y+0][x+0] = abc
|
||||||
|
z.ybr[y+0][x+1] = bcd
|
||||||
|
z.ybr[y+0][x+2] = cde
|
||||||
|
z.ybr[y+0][x+3] = def
|
||||||
|
z.ybr[y+1][x+0] = bcd
|
||||||
|
z.ybr[y+1][x+1] = cde
|
||||||
|
z.ybr[y+1][x+2] = def
|
||||||
|
z.ybr[y+1][x+3] = efg
|
||||||
|
z.ybr[y+2][x+0] = cde
|
||||||
|
z.ybr[y+2][x+1] = def
|
||||||
|
z.ybr[y+2][x+2] = efg
|
||||||
|
z.ybr[y+2][x+3] = fgh
|
||||||
|
z.ybr[y+3][x+0] = def
|
||||||
|
z.ybr[y+3][x+1] = efg
|
||||||
|
z.ybr[y+3][x+2] = fgh
|
||||||
|
z.ybr[y+3][x+3] = ghh
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4VL(z *Decoder, y, x int) {
|
||||||
|
a := int32(z.ybr[y-1][x+0])
|
||||||
|
b := int32(z.ybr[y-1][x+1])
|
||||||
|
c := int32(z.ybr[y-1][x+2])
|
||||||
|
d := int32(z.ybr[y-1][x+3])
|
||||||
|
e := int32(z.ybr[y-1][x+4])
|
||||||
|
f := int32(z.ybr[y-1][x+5])
|
||||||
|
g := int32(z.ybr[y-1][x+6])
|
||||||
|
h := int32(z.ybr[y-1][x+7])
|
||||||
|
ab := uint8((a + b + 1) / 2)
|
||||||
|
bc := uint8((b + c + 1) / 2)
|
||||||
|
cd := uint8((c + d + 1) / 2)
|
||||||
|
de := uint8((d + e + 1) / 2)
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
cde := uint8((c + 2*d + e + 2) / 4)
|
||||||
|
def := uint8((d + 2*e + f + 2) / 4)
|
||||||
|
efg := uint8((e + 2*f + g + 2) / 4)
|
||||||
|
fgh := uint8((f + 2*g + h + 2) / 4)
|
||||||
|
z.ybr[y+0][x+0] = ab
|
||||||
|
z.ybr[y+0][x+1] = bc
|
||||||
|
z.ybr[y+0][x+2] = cd
|
||||||
|
z.ybr[y+0][x+3] = de
|
||||||
|
z.ybr[y+1][x+0] = abc
|
||||||
|
z.ybr[y+1][x+1] = bcd
|
||||||
|
z.ybr[y+1][x+2] = cde
|
||||||
|
z.ybr[y+1][x+3] = def
|
||||||
|
z.ybr[y+2][x+0] = bc
|
||||||
|
z.ybr[y+2][x+1] = cd
|
||||||
|
z.ybr[y+2][x+2] = de
|
||||||
|
z.ybr[y+2][x+3] = efg
|
||||||
|
z.ybr[y+3][x+0] = bcd
|
||||||
|
z.ybr[y+3][x+1] = cde
|
||||||
|
z.ybr[y+3][x+2] = def
|
||||||
|
z.ybr[y+3][x+3] = fgh
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4HD(z *Decoder, y, x int) {
|
||||||
|
s := int32(z.ybr[y+3][x-1])
|
||||||
|
r := int32(z.ybr[y+2][x-1])
|
||||||
|
q := int32(z.ybr[y+1][x-1])
|
||||||
|
p := int32(z.ybr[y+0][x-1])
|
||||||
|
a := int32(z.ybr[y-1][x-1])
|
||||||
|
b := int32(z.ybr[y-1][x+0])
|
||||||
|
c := int32(z.ybr[y-1][x+1])
|
||||||
|
d := int32(z.ybr[y-1][x+2])
|
||||||
|
sr := uint8((s + r + 1) / 2)
|
||||||
|
rq := uint8((r + q + 1) / 2)
|
||||||
|
qp := uint8((q + p + 1) / 2)
|
||||||
|
pa := uint8((p + a + 1) / 2)
|
||||||
|
srq := uint8((s + 2*r + q + 2) / 4)
|
||||||
|
rqp := uint8((r + 2*q + p + 2) / 4)
|
||||||
|
qpa := uint8((q + 2*p + a + 2) / 4)
|
||||||
|
pab := uint8((p + 2*a + b + 2) / 4)
|
||||||
|
abc := uint8((a + 2*b + c + 2) / 4)
|
||||||
|
bcd := uint8((b + 2*c + d + 2) / 4)
|
||||||
|
z.ybr[y+0][x+0] = pa
|
||||||
|
z.ybr[y+0][x+1] = pab
|
||||||
|
z.ybr[y+0][x+2] = abc
|
||||||
|
z.ybr[y+0][x+3] = bcd
|
||||||
|
z.ybr[y+1][x+0] = qp
|
||||||
|
z.ybr[y+1][x+1] = qpa
|
||||||
|
z.ybr[y+1][x+2] = pa
|
||||||
|
z.ybr[y+1][x+3] = pab
|
||||||
|
z.ybr[y+2][x+0] = rq
|
||||||
|
z.ybr[y+2][x+1] = rqp
|
||||||
|
z.ybr[y+2][x+2] = qp
|
||||||
|
z.ybr[y+2][x+3] = qpa
|
||||||
|
z.ybr[y+3][x+0] = sr
|
||||||
|
z.ybr[y+3][x+1] = srq
|
||||||
|
z.ybr[y+3][x+2] = rq
|
||||||
|
z.ybr[y+3][x+3] = rqp
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc4HU(z *Decoder, y, x int) {
|
||||||
|
s := int32(z.ybr[y+3][x-1])
|
||||||
|
r := int32(z.ybr[y+2][x-1])
|
||||||
|
q := int32(z.ybr[y+1][x-1])
|
||||||
|
p := int32(z.ybr[y+0][x-1])
|
||||||
|
pq := uint8((p + q + 1) / 2)
|
||||||
|
qr := uint8((q + r + 1) / 2)
|
||||||
|
rs := uint8((r + s + 1) / 2)
|
||||||
|
pqr := uint8((p + 2*q + r + 2) / 4)
|
||||||
|
qrs := uint8((q + 2*r + s + 2) / 4)
|
||||||
|
rss := uint8((r + 2*s + s + 2) / 4)
|
||||||
|
sss := uint8(s)
|
||||||
|
z.ybr[y+0][x+0] = pq
|
||||||
|
z.ybr[y+0][x+1] = pqr
|
||||||
|
z.ybr[y+0][x+2] = qr
|
||||||
|
z.ybr[y+0][x+3] = qrs
|
||||||
|
z.ybr[y+1][x+0] = qr
|
||||||
|
z.ybr[y+1][x+1] = qrs
|
||||||
|
z.ybr[y+1][x+2] = rs
|
||||||
|
z.ybr[y+1][x+3] = rss
|
||||||
|
z.ybr[y+2][x+0] = rs
|
||||||
|
z.ybr[y+2][x+1] = rss
|
||||||
|
z.ybr[y+2][x+2] = sss
|
||||||
|
z.ybr[y+2][x+3] = sss
|
||||||
|
z.ybr[y+3][x+0] = sss
|
||||||
|
z.ybr[y+3][x+1] = sss
|
||||||
|
z.ybr[y+3][x+2] = sss
|
||||||
|
z.ybr[y+3][x+3] = sss
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8DC(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(8)
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
sum += uint32(z.ybr[y-1][x+i])
|
||||||
|
}
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
sum += uint32(z.ybr[y+j][x-1])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 16)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8TM(z *Decoder, y, x int) {
|
||||||
|
delta0 := -int32(z.ybr[y-1][x-1])
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
delta1 := delta0 + int32(z.ybr[y+j][x-1])
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
delta2 := delta1 + int32(z.ybr[y-1][x+i])
|
||||||
|
z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8VE(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = z.ybr[y-1][x+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8HE(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = z.ybr[y+j][x-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8DCTop(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(4)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
sum += uint32(z.ybr[y+j][x-1])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 8)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8DCLeft(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(4)
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
sum += uint32(z.ybr[y-1][x+i])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 8)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc8DCTopLeft(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
z.ybr[y+j][x+i] = 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16DC(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(16)
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
sum += uint32(z.ybr[y-1][x+i])
|
||||||
|
}
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
sum += uint32(z.ybr[y+j][x-1])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 32)
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16TM(z *Decoder, y, x int) {
|
||||||
|
delta0 := -int32(z.ybr[y-1][x-1])
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
delta1 := delta0 + int32(z.ybr[y+j][x-1])
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
delta2 := delta1 + int32(z.ybr[y-1][x+i])
|
||||||
|
z.ybr[y+j][x+i] = uint8(clip(delta2, 0, 255))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16VE(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = z.ybr[y-1][x+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16HE(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = z.ybr[y+j][x-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16DCTop(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(8)
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
sum += uint32(z.ybr[y+j][x-1])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 16)
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16DCLeft(z *Decoder, y, x int) {
|
||||||
|
sum := uint32(8)
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
sum += uint32(z.ybr[y-1][x+i])
|
||||||
|
}
|
||||||
|
avg := uint8(sum / 16)
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func predFunc16DCTopLeft(z *Decoder, y, x int) {
|
||||||
|
for j := 0; j < 16; j++ {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
z.ybr[y+j][x+i] = 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
vendor/golang.org/x/image/vp8/quant.go
generated
vendored
Normal file
98
vendor/golang.org/x/image/vp8/quant.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file implements parsing the quantization factors.
|
||||||
|
|
||||||
|
// quant are DC/AC quantization factors.
|
||||||
|
type quant struct {
|
||||||
|
y1 [2]uint16
|
||||||
|
y2 [2]uint16
|
||||||
|
uv [2]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// clip clips x to the range [min, max] inclusive.
|
||||||
|
func clip(x, min, max int32) int32 {
|
||||||
|
if x < min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
if x > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseQuant parses the quantization factors, as specified in section 9.6.
|
||||||
|
func (d *Decoder) parseQuant() {
|
||||||
|
baseQ0 := d.fp.readUint(uniformProb, 7)
|
||||||
|
dqy1DC := d.fp.readOptionalInt(uniformProb, 4)
|
||||||
|
const dqy1AC = 0
|
||||||
|
dqy2DC := d.fp.readOptionalInt(uniformProb, 4)
|
||||||
|
dqy2AC := d.fp.readOptionalInt(uniformProb, 4)
|
||||||
|
dquvDC := d.fp.readOptionalInt(uniformProb, 4)
|
||||||
|
dquvAC := d.fp.readOptionalInt(uniformProb, 4)
|
||||||
|
for i := 0; i < nSegment; i++ {
|
||||||
|
q := int32(baseQ0)
|
||||||
|
if d.segmentHeader.useSegment {
|
||||||
|
if d.segmentHeader.relativeDelta {
|
||||||
|
q += int32(d.segmentHeader.quantizer[i])
|
||||||
|
} else {
|
||||||
|
q = int32(d.segmentHeader.quantizer[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.quant[i].y1[0] = dequantTableDC[clip(q+dqy1DC, 0, 127)]
|
||||||
|
d.quant[i].y1[1] = dequantTableAC[clip(q+dqy1AC, 0, 127)]
|
||||||
|
d.quant[i].y2[0] = dequantTableDC[clip(q+dqy2DC, 0, 127)] * 2
|
||||||
|
d.quant[i].y2[1] = dequantTableAC[clip(q+dqy2AC, 0, 127)] * 155 / 100
|
||||||
|
if d.quant[i].y2[1] < 8 {
|
||||||
|
d.quant[i].y2[1] = 8
|
||||||
|
}
|
||||||
|
// The 117 is not a typo. The dequant_init function in the spec's Reference
|
||||||
|
// Decoder Source Code (http://tools.ietf.org/html/rfc6386#section-9.6 Page 145)
|
||||||
|
// says to clamp the LHS value at 132, which is equal to dequantTableDC[117].
|
||||||
|
d.quant[i].uv[0] = dequantTableDC[clip(q+dquvDC, 0, 117)]
|
||||||
|
d.quant[i].uv[1] = dequantTableAC[clip(q+dquvAC, 0, 127)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dequantization tables are specified in section 14.1.
|
||||||
|
var (
|
||||||
|
dequantTableDC = [128]uint16{
|
||||||
|
4, 5, 6, 7, 8, 9, 10, 10,
|
||||||
|
11, 12, 13, 14, 15, 16, 17, 17,
|
||||||
|
18, 19, 20, 20, 21, 21, 22, 22,
|
||||||
|
23, 23, 24, 25, 25, 26, 27, 28,
|
||||||
|
29, 30, 31, 32, 33, 34, 35, 36,
|
||||||
|
37, 37, 38, 39, 40, 41, 42, 43,
|
||||||
|
44, 45, 46, 46, 47, 48, 49, 50,
|
||||||
|
51, 52, 53, 54, 55, 56, 57, 58,
|
||||||
|
59, 60, 61, 62, 63, 64, 65, 66,
|
||||||
|
67, 68, 69, 70, 71, 72, 73, 74,
|
||||||
|
75, 76, 76, 77, 78, 79, 80, 81,
|
||||||
|
82, 83, 84, 85, 86, 87, 88, 89,
|
||||||
|
91, 93, 95, 96, 98, 100, 101, 102,
|
||||||
|
104, 106, 108, 110, 112, 114, 116, 118,
|
||||||
|
122, 124, 126, 128, 130, 132, 134, 136,
|
||||||
|
138, 140, 143, 145, 148, 151, 154, 157,
|
||||||
|
}
|
||||||
|
dequantTableAC = [128]uint16{
|
||||||
|
4, 5, 6, 7, 8, 9, 10, 11,
|
||||||
|
12, 13, 14, 15, 16, 17, 18, 19,
|
||||||
|
20, 21, 22, 23, 24, 25, 26, 27,
|
||||||
|
28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
|
36, 37, 38, 39, 40, 41, 42, 43,
|
||||||
|
44, 45, 46, 47, 48, 49, 50, 51,
|
||||||
|
52, 53, 54, 55, 56, 57, 58, 60,
|
||||||
|
62, 64, 66, 68, 70, 72, 74, 76,
|
||||||
|
78, 80, 82, 84, 86, 88, 90, 92,
|
||||||
|
94, 96, 98, 100, 102, 104, 106, 108,
|
||||||
|
110, 112, 114, 116, 119, 122, 125, 128,
|
||||||
|
131, 134, 137, 140, 143, 146, 149, 152,
|
||||||
|
155, 158, 161, 164, 167, 170, 173, 177,
|
||||||
|
181, 185, 189, 193, 197, 201, 205, 209,
|
||||||
|
213, 217, 221, 225, 229, 234, 239, 245,
|
||||||
|
249, 254, 259, 264, 269, 274, 279, 284,
|
||||||
|
}
|
||||||
|
)
|
||||||
442
vendor/golang.org/x/image/vp8/reconstruct.go
generated
vendored
Normal file
442
vendor/golang.org/x/image/vp8/reconstruct.go
generated
vendored
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file implements decoding DCT/WHT residual coefficients and
|
||||||
|
// reconstructing YCbCr data equal to predicted values plus residuals.
|
||||||
|
//
|
||||||
|
// There are 1*16*16 + 2*8*8 + 1*4*4 coefficients per macroblock:
|
||||||
|
// - 1*16*16 luma DCT coefficients,
|
||||||
|
// - 2*8*8 chroma DCT coefficients, and
|
||||||
|
// - 1*4*4 luma WHT coefficients.
|
||||||
|
// Coefficients are read in lots of 16, and the later coefficients in each lot
|
||||||
|
// are often zero.
|
||||||
|
//
|
||||||
|
// The YCbCr data consists of 1*16*16 luma values and 2*8*8 chroma values,
|
||||||
|
// plus previously decoded values along the top and left borders. The combined
|
||||||
|
// values are laid out as a [1+16+1+8][32]uint8 so that vertically adjacent
|
||||||
|
// samples are 32 bytes apart. In detail, the layout is:
|
||||||
|
//
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// . . . . . . . a b b b b b b b b b b b b b b b b c c c c . . . . 0
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 1
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 2
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 3
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 4
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 5
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 6
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 7
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 8
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 9
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 10
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 11
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y c c c c . . . . 12
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 13
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 14
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 15
|
||||||
|
// . . . . . . . d Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . 16
|
||||||
|
// . . . . . . . e f f f f f f f f . . . . . . . g h h h h h h h h 17
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 18
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 19
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 20
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 21
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 22
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 23
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 24
|
||||||
|
// . . . . . . . i B B B B B B B B . . . . . . . j R R R R R R R R 25
|
||||||
|
//
|
||||||
|
// Y, B and R are the reconstructed luma (Y) and chroma (B, R) values.
|
||||||
|
// The Y values are predicted (either as one 16x16 region or 16 4x4 regions)
|
||||||
|
// based on the row above's Y values (some combination of {abc} or {dYC}) and
|
||||||
|
// the column left's Y values (either {ad} or {bY}). Similarly, B and R values
|
||||||
|
// are predicted on the row above and column left of their respective 8x8
|
||||||
|
// region: {efi} for B, {ghj} for R.
|
||||||
|
//
|
||||||
|
// For uppermost macroblocks (i.e. those with mby == 0), the {abcefgh} values
|
||||||
|
// are initialized to 0x81. Otherwise, they are copied from the bottom row of
|
||||||
|
// the macroblock above. The {c} values are then duplicated from row 0 to rows
|
||||||
|
// 4, 8 and 12 of the ybr workspace.
|
||||||
|
// Similarly, for leftmost macroblocks (i.e. those with mbx == 0), the {adeigj}
|
||||||
|
// values are initialized to 0x7f. Otherwise, they are copied from the right
|
||||||
|
// column of the macroblock to the left.
|
||||||
|
// For the top-left macroblock (with mby == 0 && mbx == 0), {aeg} is 0x81.
|
||||||
|
//
|
||||||
|
// When moving from one macroblock to the next horizontally, the {adeigj}
|
||||||
|
// values can simply be copied from the workspace to itself, shifted by 8 or
|
||||||
|
// 16 columns. When moving from one macroblock to the next vertically,
|
||||||
|
// filtering can occur and hence the row values have to be copied from the
|
||||||
|
// post-filtered image instead of the pre-filtered workspace.
|
||||||
|
|
||||||
|
const (
|
||||||
|
bCoeffBase = 1*16*16 + 0*8*8
|
||||||
|
rCoeffBase = 1*16*16 + 1*8*8
|
||||||
|
whtCoeffBase = 1*16*16 + 2*8*8
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ybrYX = 8
|
||||||
|
ybrYY = 1
|
||||||
|
ybrBX = 8
|
||||||
|
ybrBY = 18
|
||||||
|
ybrRX = 24
|
||||||
|
ybrRY = 18
|
||||||
|
)
|
||||||
|
|
||||||
|
// prepareYBR prepares the {abcdefghij} elements of ybr.
|
||||||
|
func (d *Decoder) prepareYBR(mbx, mby int) {
|
||||||
|
if mbx == 0 {
|
||||||
|
for y := 0; y < 17; y++ {
|
||||||
|
d.ybr[y][7] = 0x81
|
||||||
|
}
|
||||||
|
for y := 17; y < 26; y++ {
|
||||||
|
d.ybr[y][7] = 0x81
|
||||||
|
d.ybr[y][23] = 0x81
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for y := 0; y < 17; y++ {
|
||||||
|
d.ybr[y][7] = d.ybr[y][7+16]
|
||||||
|
}
|
||||||
|
for y := 17; y < 26; y++ {
|
||||||
|
d.ybr[y][7] = d.ybr[y][15]
|
||||||
|
d.ybr[y][23] = d.ybr[y][31]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mby == 0 {
|
||||||
|
for x := 7; x < 28; x++ {
|
||||||
|
d.ybr[0][x] = 0x7f
|
||||||
|
}
|
||||||
|
for x := 7; x < 16; x++ {
|
||||||
|
d.ybr[17][x] = 0x7f
|
||||||
|
}
|
||||||
|
for x := 23; x < 32; x++ {
|
||||||
|
d.ybr[17][x] = 0x7f
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+i]
|
||||||
|
}
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
d.ybr[17][8+i] = d.img.Cb[(8*mby-1)*d.img.CStride+8*mbx+i]
|
||||||
|
}
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
d.ybr[17][24+i] = d.img.Cr[(8*mby-1)*d.img.CStride+8*mbx+i]
|
||||||
|
}
|
||||||
|
if mbx == d.mbw-1 {
|
||||||
|
for i := 16; i < 20; i++ {
|
||||||
|
d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+15]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 16; i < 20; i++ {
|
||||||
|
d.ybr[0][8+i] = d.img.Y[(16*mby-1)*d.img.YStride+16*mbx+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for y := 4; y < 16; y += 4 {
|
||||||
|
d.ybr[y][24] = d.ybr[0][24]
|
||||||
|
d.ybr[y][25] = d.ybr[0][25]
|
||||||
|
d.ybr[y][26] = d.ybr[0][26]
|
||||||
|
d.ybr[y][27] = d.ybr[0][27]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// btou converts a bool to a 0/1 value.
|
||||||
|
func btou(b bool) uint8 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack packs four 0/1 values into four bits of a uint32.
|
||||||
|
func pack(x [4]uint8, shift int) uint32 {
|
||||||
|
u := uint32(x[0])<<0 | uint32(x[1])<<1 | uint32(x[2])<<2 | uint32(x[3])<<3
|
||||||
|
return u << uint(shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack unpacks four 0/1 values from a four-bit value.
|
||||||
|
var unpack = [16][4]uint8{
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
{0, 1, 0, 0},
|
||||||
|
{1, 1, 0, 0},
|
||||||
|
{0, 0, 1, 0},
|
||||||
|
{1, 0, 1, 0},
|
||||||
|
{0, 1, 1, 0},
|
||||||
|
{1, 1, 1, 0},
|
||||||
|
{0, 0, 0, 1},
|
||||||
|
{1, 0, 0, 1},
|
||||||
|
{0, 1, 0, 1},
|
||||||
|
{1, 1, 0, 1},
|
||||||
|
{0, 0, 1, 1},
|
||||||
|
{1, 0, 1, 1},
|
||||||
|
{0, 1, 1, 1},
|
||||||
|
{1, 1, 1, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The mapping from 4x4 region position to band is specified in section 13.3.
|
||||||
|
bands = [17]uint8{0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0}
|
||||||
|
// Category probabilties are specified in section 13.2.
|
||||||
|
// Decoding categories 1 and 2 are done inline.
|
||||||
|
cat3456 = [4][12]uint8{
|
||||||
|
{173, 148, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{176, 155, 140, 135, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{180, 157, 141, 134, 130, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0},
|
||||||
|
}
|
||||||
|
// The zigzag order is:
|
||||||
|
// 0 1 5 6
|
||||||
|
// 2 4 7 12
|
||||||
|
// 3 8 11 13
|
||||||
|
// 9 10 14 15
|
||||||
|
zigzag = [16]uint8{0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15}
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseResiduals4 parses a 4x4 region of residual coefficients, as specified
|
||||||
|
// in section 13.3, and returns a 0/1 value indicating whether there was at
|
||||||
|
// least one non-zero coefficient.
|
||||||
|
// r is the partition to read bits from.
|
||||||
|
// plane and context describe which token probability table to use. context is
|
||||||
|
// either 0, 1 or 2, and equals how many of the macroblock left and macroblock
|
||||||
|
// above have non-zero coefficients.
|
||||||
|
// quant are the DC/AC quantization factors.
|
||||||
|
// skipFirstCoeff is whether the DC coefficient has already been parsed.
|
||||||
|
// coeffBase is the base index of d.coeff to write to.
|
||||||
|
func (d *Decoder) parseResiduals4(r *partition, plane int, context uint8, quant [2]uint16, skipFirstCoeff bool, coeffBase int) uint8 {
|
||||||
|
prob, n := &d.tokenProb[plane], 0
|
||||||
|
if skipFirstCoeff {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
p := prob[bands[n]][context]
|
||||||
|
if !r.readBit(p[0]) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for n != 16 {
|
||||||
|
n++
|
||||||
|
if !r.readBit(p[1]) {
|
||||||
|
p = prob[bands[n]][0]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var v uint32
|
||||||
|
if !r.readBit(p[2]) {
|
||||||
|
v = 1
|
||||||
|
p = prob[bands[n]][1]
|
||||||
|
} else {
|
||||||
|
if !r.readBit(p[3]) {
|
||||||
|
if !r.readBit(p[4]) {
|
||||||
|
v = 2
|
||||||
|
} else {
|
||||||
|
v = 3 + r.readUint(p[5], 1)
|
||||||
|
}
|
||||||
|
} else if !r.readBit(p[6]) {
|
||||||
|
if !r.readBit(p[7]) {
|
||||||
|
// Category 1.
|
||||||
|
v = 5 + r.readUint(159, 1)
|
||||||
|
} else {
|
||||||
|
// Category 2.
|
||||||
|
v = 7 + 2*r.readUint(165, 1) + r.readUint(145, 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Categories 3, 4, 5 or 6.
|
||||||
|
b1 := r.readUint(p[8], 1)
|
||||||
|
b0 := r.readUint(p[9+b1], 1)
|
||||||
|
cat := 2*b1 + b0
|
||||||
|
tab := &cat3456[cat]
|
||||||
|
v = 0
|
||||||
|
for i := 0; tab[i] != 0; i++ {
|
||||||
|
v *= 2
|
||||||
|
v += r.readUint(tab[i], 1)
|
||||||
|
}
|
||||||
|
v += 3 + (8 << cat)
|
||||||
|
}
|
||||||
|
p = prob[bands[n]][2]
|
||||||
|
}
|
||||||
|
z := zigzag[n-1]
|
||||||
|
c := int32(v) * int32(quant[btou(z > 0)])
|
||||||
|
if r.readBit(uniformProb) {
|
||||||
|
c = -c
|
||||||
|
}
|
||||||
|
d.coeff[coeffBase+int(z)] = int16(c)
|
||||||
|
if n == 16 || !r.readBit(p[0]) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseResiduals parses the residuals and returns whether inner loop filtering
|
||||||
|
// should be skipped for this macroblock.
|
||||||
|
func (d *Decoder) parseResiduals(mbx, mby int) (skip bool) {
|
||||||
|
partition := &d.op[mby&(d.nOP-1)]
|
||||||
|
plane := planeY1SansY2
|
||||||
|
quant := &d.quant[d.segment]
|
||||||
|
|
||||||
|
// Parse the DC coefficient of each 4x4 luma region.
|
||||||
|
if d.usePredY16 {
|
||||||
|
nz := d.parseResiduals4(partition, planeY2, d.leftMB.nzY16+d.upMB[mbx].nzY16, quant.y2, false, whtCoeffBase)
|
||||||
|
d.leftMB.nzY16 = nz
|
||||||
|
d.upMB[mbx].nzY16 = nz
|
||||||
|
d.inverseWHT16()
|
||||||
|
plane = planeY1WithY2
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nzDC, nzAC [4]uint8
|
||||||
|
nzDCMask, nzACMask uint32
|
||||||
|
coeffBase int
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse the luma coefficients.
|
||||||
|
lnz := unpack[d.leftMB.nzMask&0x0f]
|
||||||
|
unz := unpack[d.upMB[mbx].nzMask&0x0f]
|
||||||
|
for y := 0; y < 4; y++ {
|
||||||
|
nz := lnz[y]
|
||||||
|
for x := 0; x < 4; x++ {
|
||||||
|
nz = d.parseResiduals4(partition, plane, nz+unz[x], quant.y1, d.usePredY16, coeffBase)
|
||||||
|
unz[x] = nz
|
||||||
|
nzAC[x] = nz
|
||||||
|
nzDC[x] = btou(d.coeff[coeffBase] != 0)
|
||||||
|
coeffBase += 16
|
||||||
|
}
|
||||||
|
lnz[y] = nz
|
||||||
|
nzDCMask |= pack(nzDC, y*4)
|
||||||
|
nzACMask |= pack(nzAC, y*4)
|
||||||
|
}
|
||||||
|
lnzMask := pack(lnz, 0)
|
||||||
|
unzMask := pack(unz, 0)
|
||||||
|
|
||||||
|
// Parse the chroma coefficients.
|
||||||
|
lnz = unpack[d.leftMB.nzMask>>4]
|
||||||
|
unz = unpack[d.upMB[mbx].nzMask>>4]
|
||||||
|
for c := 0; c < 4; c += 2 {
|
||||||
|
for y := 0; y < 2; y++ {
|
||||||
|
nz := lnz[y+c]
|
||||||
|
for x := 0; x < 2; x++ {
|
||||||
|
nz = d.parseResiduals4(partition, planeUV, nz+unz[x+c], quant.uv, false, coeffBase)
|
||||||
|
unz[x+c] = nz
|
||||||
|
nzAC[y*2+x] = nz
|
||||||
|
nzDC[y*2+x] = btou(d.coeff[coeffBase] != 0)
|
||||||
|
coeffBase += 16
|
||||||
|
}
|
||||||
|
lnz[y+c] = nz
|
||||||
|
}
|
||||||
|
nzDCMask |= pack(nzDC, 16+c*2)
|
||||||
|
nzACMask |= pack(nzAC, 16+c*2)
|
||||||
|
}
|
||||||
|
lnzMask |= pack(lnz, 4)
|
||||||
|
unzMask |= pack(unz, 4)
|
||||||
|
|
||||||
|
// Save decoder state.
|
||||||
|
d.leftMB.nzMask = uint8(lnzMask)
|
||||||
|
d.upMB[mbx].nzMask = uint8(unzMask)
|
||||||
|
d.nzDCMask = nzDCMask
|
||||||
|
d.nzACMask = nzACMask
|
||||||
|
|
||||||
|
// Section 15.1 of the spec says that "Steps 2 and 4 [of the loop filter]
|
||||||
|
// are skipped... [if] there is no DCT coefficient coded for the whole
|
||||||
|
// macroblock."
|
||||||
|
return nzDCMask == 0 && nzACMask == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconstructMacroblock applies the predictor functions and adds the inverse-
|
||||||
|
// DCT transformed residuals to recover the YCbCr data.
|
||||||
|
func (d *Decoder) reconstructMacroblock(mbx, mby int) {
|
||||||
|
if d.usePredY16 {
|
||||||
|
p := checkTopLeftPred(mbx, mby, d.predY16)
|
||||||
|
predFunc16[p](d, 1, 8)
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
n := 4*j + i
|
||||||
|
y := 4*j + 1
|
||||||
|
x := 4*i + 8
|
||||||
|
mask := uint32(1) << uint(n)
|
||||||
|
if d.nzACMask&mask != 0 {
|
||||||
|
d.inverseDCT4(y, x, 16*n)
|
||||||
|
} else if d.nzDCMask&mask != 0 {
|
||||||
|
d.inverseDCT4DCOnly(y, x, 16*n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
n := 4*j + i
|
||||||
|
y := 4*j + 1
|
||||||
|
x := 4*i + 8
|
||||||
|
predFunc4[d.predY4[j][i]](d, y, x)
|
||||||
|
mask := uint32(1) << uint(n)
|
||||||
|
if d.nzACMask&mask != 0 {
|
||||||
|
d.inverseDCT4(y, x, 16*n)
|
||||||
|
} else if d.nzDCMask&mask != 0 {
|
||||||
|
d.inverseDCT4DCOnly(y, x, 16*n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p := checkTopLeftPred(mbx, mby, d.predC8)
|
||||||
|
predFunc8[p](d, ybrBY, ybrBX)
|
||||||
|
if d.nzACMask&0x0f0000 != 0 {
|
||||||
|
d.inverseDCT8(ybrBY, ybrBX, bCoeffBase)
|
||||||
|
} else if d.nzDCMask&0x0f0000 != 0 {
|
||||||
|
d.inverseDCT8DCOnly(ybrBY, ybrBX, bCoeffBase)
|
||||||
|
}
|
||||||
|
predFunc8[p](d, ybrRY, ybrRX)
|
||||||
|
if d.nzACMask&0xf00000 != 0 {
|
||||||
|
d.inverseDCT8(ybrRY, ybrRX, rCoeffBase)
|
||||||
|
} else if d.nzDCMask&0xf00000 != 0 {
|
||||||
|
d.inverseDCT8DCOnly(ybrRY, ybrRX, rCoeffBase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconstruct reconstructs one macroblock and returns whether inner loop
|
||||||
|
// filtering should be skipped for it.
|
||||||
|
func (d *Decoder) reconstruct(mbx, mby int) (skip bool) {
|
||||||
|
if d.segmentHeader.updateMap {
|
||||||
|
if !d.fp.readBit(d.segmentHeader.prob[0]) {
|
||||||
|
d.segment = int(d.fp.readUint(d.segmentHeader.prob[1], 1))
|
||||||
|
} else {
|
||||||
|
d.segment = int(d.fp.readUint(d.segmentHeader.prob[2], 1)) + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.useSkipProb {
|
||||||
|
skip = d.fp.readBit(d.skipProb)
|
||||||
|
}
|
||||||
|
// Prepare the workspace.
|
||||||
|
for i := range d.coeff {
|
||||||
|
d.coeff[i] = 0
|
||||||
|
}
|
||||||
|
d.prepareYBR(mbx, mby)
|
||||||
|
// Parse the predictor modes.
|
||||||
|
d.usePredY16 = d.fp.readBit(145)
|
||||||
|
if d.usePredY16 {
|
||||||
|
d.parsePredModeY16(mbx)
|
||||||
|
} else {
|
||||||
|
d.parsePredModeY4(mbx)
|
||||||
|
}
|
||||||
|
d.parsePredModeC8()
|
||||||
|
// Parse the residuals.
|
||||||
|
if !skip {
|
||||||
|
skip = d.parseResiduals(mbx, mby)
|
||||||
|
} else {
|
||||||
|
if d.usePredY16 {
|
||||||
|
d.leftMB.nzY16 = 0
|
||||||
|
d.upMB[mbx].nzY16 = 0
|
||||||
|
}
|
||||||
|
d.leftMB.nzMask = 0
|
||||||
|
d.upMB[mbx].nzMask = 0
|
||||||
|
d.nzDCMask = 0
|
||||||
|
d.nzACMask = 0
|
||||||
|
}
|
||||||
|
// Reconstruct the YCbCr data and copy it to the image.
|
||||||
|
d.reconstructMacroblock(mbx, mby)
|
||||||
|
for i, y := (mby*d.img.YStride+mbx)*16, 0; y < 16; i, y = i+d.img.YStride, y+1 {
|
||||||
|
copy(d.img.Y[i:i+16], d.ybr[ybrYY+y][ybrYX:ybrYX+16])
|
||||||
|
}
|
||||||
|
for i, y := (mby*d.img.CStride+mbx)*8, 0; y < 8; i, y = i+d.img.CStride, y+1 {
|
||||||
|
copy(d.img.Cb[i:i+8], d.ybr[ybrBY+y][ybrBX:ybrBX+8])
|
||||||
|
copy(d.img.Cr[i:i+8], d.ybr[ybrRY+y][ybrRX:ybrRX+8])
|
||||||
|
}
|
||||||
|
return skip
|
||||||
|
}
|
||||||
381
vendor/golang.org/x/image/vp8/token.go
generated
vendored
Normal file
381
vendor/golang.org/x/image/vp8/token.go
generated
vendored
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8
|
||||||
|
|
||||||
|
// This file contains token probabilities for decoding DCT/WHT coefficients, as
|
||||||
|
// specified in chapter 13.
|
||||||
|
|
||||||
|
func (d *Decoder) parseTokenProb() {
|
||||||
|
for i := range d.tokenProb {
|
||||||
|
for j := range d.tokenProb[i] {
|
||||||
|
for k := range d.tokenProb[i][j] {
|
||||||
|
for l := range d.tokenProb[i][j][k] {
|
||||||
|
if d.fp.readBit(tokenProbUpdateProb[i][j][k][l]) {
|
||||||
|
d.tokenProb[i][j][k][l] = uint8(d.fp.readUint(uniformProb, 8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The plane enumeration is specified in section 13.3.
|
||||||
|
const (
|
||||||
|
planeY1WithY2 = iota
|
||||||
|
planeY2
|
||||||
|
planeUV
|
||||||
|
planeY1SansY2
|
||||||
|
nPlane
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nBand = 8
|
||||||
|
nContext = 3
|
||||||
|
nProb = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token probability update probabilities are specified in section 13.4.
|
||||||
|
var tokenProbUpdateProb = [nPlane][nBand][nContext][nProb]uint8{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255},
|
||||||
|
{250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255},
|
||||||
|
{234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default token probabilities are specified in section 13.5.
|
||||||
|
var defaultTokenProb = [nPlane][nBand][nContext][nProb]uint8{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128},
|
||||||
|
{189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128},
|
||||||
|
{106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128},
|
||||||
|
{181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128},
|
||||||
|
{78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128},
|
||||||
|
{184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128},
|
||||||
|
{77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128},
|
||||||
|
{170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128},
|
||||||
|
{37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128},
|
||||||
|
{207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128},
|
||||||
|
{102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128},
|
||||||
|
{177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128},
|
||||||
|
{80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62},
|
||||||
|
{131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1},
|
||||||
|
{68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128},
|
||||||
|
{184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128},
|
||||||
|
{81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128},
|
||||||
|
{99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128},
|
||||||
|
{23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128},
|
||||||
|
{109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128},
|
||||||
|
{44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128},
|
||||||
|
{94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128},
|
||||||
|
{22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128},
|
||||||
|
{124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128},
|
||||||
|
{35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128},
|
||||||
|
{121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128},
|
||||||
|
{45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128},
|
||||||
|
{203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
{137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128},
|
||||||
|
{175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128},
|
||||||
|
{73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128},
|
||||||
|
{239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128},
|
||||||
|
{155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128},
|
||||||
|
{201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128},
|
||||||
|
{69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
{223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128},
|
||||||
|
{141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
{190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128},
|
||||||
|
{149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
{213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128},
|
||||||
|
{55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255},
|
||||||
|
{126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128},
|
||||||
|
{61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128},
|
||||||
|
{166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128},
|
||||||
|
{39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128},
|
||||||
|
{124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128},
|
||||||
|
{24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128},
|
||||||
|
{149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128},
|
||||||
|
{28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128},
|
||||||
|
{123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128},
|
||||||
|
{20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128},
|
||||||
|
{168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128},
|
||||||
|
{47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128},
|
||||||
|
{141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128},
|
||||||
|
{42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
{238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
603
vendor/golang.org/x/image/vp8l/decode.go
generated
vendored
Normal file
603
vendor/golang.org/x/image/vp8l/decode.go
generated
vendored
Normal file
@@ -0,0 +1,603 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package vp8l implements a decoder for the VP8L lossless image format.
|
||||||
|
//
|
||||||
|
// The VP8L specification is at:
|
||||||
|
// https://developers.google.com/speed/webp/docs/riff_container
|
||||||
|
package vp8l // import "golang.org/x/image/vp8l"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidCodeLengths = errors.New("vp8l: invalid code lengths")
|
||||||
|
errInvalidHuffmanTree = errors.New("vp8l: invalid Huffman tree")
|
||||||
|
)
|
||||||
|
|
||||||
|
// colorCacheMultiplier is the multiplier used for the color cache hash
|
||||||
|
// function, specified in section 4.2.3.
|
||||||
|
const colorCacheMultiplier = 0x1e35a7bd
|
||||||
|
|
||||||
|
// distanceMapTable is the look-up table for distanceMap.
|
||||||
|
var distanceMapTable = [120]uint8{
|
||||||
|
0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
|
||||||
|
0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
|
||||||
|
0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
|
||||||
|
0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
|
||||||
|
0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
|
||||||
|
0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
|
||||||
|
0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
|
||||||
|
0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
|
||||||
|
0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
|
||||||
|
0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
|
||||||
|
0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
|
||||||
|
0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70,
|
||||||
|
}
|
||||||
|
|
||||||
|
// distanceMap maps a LZ77 backwards reference distance to a two-dimensional
|
||||||
|
// pixel offset, specified in section 4.2.2.
|
||||||
|
func distanceMap(w int32, code uint32) int32 {
|
||||||
|
if int32(code) > int32(len(distanceMapTable)) {
|
||||||
|
return int32(code) - int32(len(distanceMapTable))
|
||||||
|
}
|
||||||
|
distCode := int32(distanceMapTable[code-1])
|
||||||
|
yOffset := distCode >> 4
|
||||||
|
xOffset := 8 - distCode&0xf
|
||||||
|
if d := yOffset*w + xOffset; d >= 1 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoder holds the bit-stream for a VP8L image.
|
||||||
|
type decoder struct {
|
||||||
|
r io.ByteReader
|
||||||
|
bits uint32
|
||||||
|
nBits uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// read reads the next n bits from the decoder's bit-stream.
|
||||||
|
func (d *decoder) read(n uint32) (uint32, error) {
|
||||||
|
for d.nBits < n {
|
||||||
|
c, err := d.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d.bits |= uint32(c) << d.nBits
|
||||||
|
d.nBits += 8
|
||||||
|
}
|
||||||
|
u := d.bits & (1<<n - 1)
|
||||||
|
d.bits >>= n
|
||||||
|
d.nBits -= n
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeTransform decodes the next transform and the width of the image after
|
||||||
|
// transformation (or equivalently, before inverse transformation), specified
|
||||||
|
// in section 3.
|
||||||
|
func (d *decoder) decodeTransform(w int32, h int32) (t transform, newWidth int32, err error) {
|
||||||
|
t.oldWidth = w
|
||||||
|
t.transformType, err = d.read(2)
|
||||||
|
if err != nil {
|
||||||
|
return transform{}, 0, err
|
||||||
|
}
|
||||||
|
switch t.transformType {
|
||||||
|
case transformTypePredictor, transformTypeCrossColor:
|
||||||
|
t.bits, err = d.read(3)
|
||||||
|
if err != nil {
|
||||||
|
return transform{}, 0, err
|
||||||
|
}
|
||||||
|
t.bits += 2
|
||||||
|
t.pix, err = d.decodePix(nTiles(w, t.bits), nTiles(h, t.bits), 0, false)
|
||||||
|
if err != nil {
|
||||||
|
return transform{}, 0, err
|
||||||
|
}
|
||||||
|
case transformTypeSubtractGreen:
|
||||||
|
// No-op.
|
||||||
|
case transformTypeColorIndexing:
|
||||||
|
nColors, err := d.read(8)
|
||||||
|
if err != nil {
|
||||||
|
return transform{}, 0, err
|
||||||
|
}
|
||||||
|
nColors++
|
||||||
|
t.bits = 0
|
||||||
|
switch {
|
||||||
|
case nColors <= 2:
|
||||||
|
t.bits = 3
|
||||||
|
case nColors <= 4:
|
||||||
|
t.bits = 2
|
||||||
|
case nColors <= 16:
|
||||||
|
t.bits = 1
|
||||||
|
}
|
||||||
|
w = nTiles(w, t.bits)
|
||||||
|
pix, err := d.decodePix(int32(nColors), 1, 4*256, false)
|
||||||
|
if err != nil {
|
||||||
|
return transform{}, 0, err
|
||||||
|
}
|
||||||
|
for p := 4; p < len(pix); p += 4 {
|
||||||
|
pix[p+0] += pix[p-4]
|
||||||
|
pix[p+1] += pix[p-3]
|
||||||
|
pix[p+2] += pix[p-2]
|
||||||
|
pix[p+3] += pix[p-1]
|
||||||
|
}
|
||||||
|
// The spec says that "if the index is equal or larger than color_table_size,
|
||||||
|
// the argb color value should be set to 0x00000000 (transparent black)."
|
||||||
|
// We re-slice up to 256 4-byte pixels.
|
||||||
|
t.pix = pix[:4*256]
|
||||||
|
}
|
||||||
|
return t, w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeatsCodeLength is the minimum code length for repeated codes.
|
||||||
|
const repeatsCodeLength = 16
|
||||||
|
|
||||||
|
// These magic numbers are specified at the end of section 5.2.2.
|
||||||
|
// The 3-length arrays apply to code lengths >= repeatsCodeLength.
|
||||||
|
var (
|
||||||
|
codeLengthCodeOrder = [19]uint8{
|
||||||
|
17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
}
|
||||||
|
repeatBits = [3]uint8{2, 3, 7}
|
||||||
|
repeatOffsets = [3]uint8{3, 3, 11}
|
||||||
|
)
|
||||||
|
|
||||||
|
// decodeCodeLengths decodes a Huffman tree's code lengths which are themselves
|
||||||
|
// encoded via a Huffman tree, specified in section 5.2.2.
|
||||||
|
func (d *decoder) decodeCodeLengths(dst []uint32, codeLengthCodeLengths []uint32) error {
|
||||||
|
h := hTree{}
|
||||||
|
if err := h.build(codeLengthCodeLengths); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
maxSymbol := len(dst)
|
||||||
|
useLength, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if useLength != 0 {
|
||||||
|
n, err := d.read(3)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n = 2 + 2*n
|
||||||
|
ms, err := d.read(n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
maxSymbol = int(ms) + 2
|
||||||
|
if maxSymbol > len(dst) {
|
||||||
|
return errInvalidCodeLengths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The spec says that "if code 16 [meaning repeat] is used before
|
||||||
|
// a non-zero value has been emitted, a value of 8 is repeated."
|
||||||
|
prevCodeLength := uint32(8)
|
||||||
|
|
||||||
|
for symbol := 0; symbol < len(dst); {
|
||||||
|
if maxSymbol == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
maxSymbol--
|
||||||
|
codeLength, err := h.next(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if codeLength < repeatsCodeLength {
|
||||||
|
dst[symbol] = codeLength
|
||||||
|
symbol++
|
||||||
|
if codeLength != 0 {
|
||||||
|
prevCodeLength = codeLength
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat, err := d.read(uint32(repeatBits[codeLength-repeatsCodeLength]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repeat += uint32(repeatOffsets[codeLength-repeatsCodeLength])
|
||||||
|
if symbol+int(repeat) > len(dst) {
|
||||||
|
return errInvalidCodeLengths
|
||||||
|
}
|
||||||
|
// A code length of 16 repeats the previous non-zero code.
|
||||||
|
// A code length of 17 or 18 repeats zeroes.
|
||||||
|
cl := uint32(0)
|
||||||
|
if codeLength == 16 {
|
||||||
|
cl = prevCodeLength
|
||||||
|
}
|
||||||
|
for ; repeat > 0; repeat-- {
|
||||||
|
dst[symbol] = cl
|
||||||
|
symbol++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeHuffmanTree decodes a Huffman tree into h.
|
||||||
|
func (d *decoder) decodeHuffmanTree(h *hTree, alphabetSize uint32) error {
|
||||||
|
useSimple, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if useSimple != 0 {
|
||||||
|
nSymbols, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nSymbols++
|
||||||
|
firstSymbolLengthCode, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
firstSymbolLengthCode = 7*firstSymbolLengthCode + 1
|
||||||
|
var symbols [2]uint32
|
||||||
|
symbols[0], err = d.read(firstSymbolLengthCode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nSymbols == 2 {
|
||||||
|
symbols[1], err = d.read(8)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h.buildSimple(nSymbols, symbols, alphabetSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
nCodes, err := d.read(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nCodes += 4
|
||||||
|
if int(nCodes) > len(codeLengthCodeOrder) {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
codeLengthCodeLengths := [len(codeLengthCodeOrder)]uint32{}
|
||||||
|
for i := uint32(0); i < nCodes; i++ {
|
||||||
|
codeLengthCodeLengths[codeLengthCodeOrder[i]], err = d.read(3)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
codeLengths := make([]uint32, alphabetSize)
|
||||||
|
if err = d.decodeCodeLengths(codeLengths, codeLengthCodeLengths[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return h.build(codeLengths)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
huffGreen = 0
|
||||||
|
huffRed = 1
|
||||||
|
huffBlue = 2
|
||||||
|
huffAlpha = 3
|
||||||
|
huffDistance = 4
|
||||||
|
nHuff = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// hGroup is an array of 5 Huffman trees.
|
||||||
|
type hGroup [nHuff]hTree
|
||||||
|
|
||||||
|
// decodeHuffmanGroups decodes the one or more hGroups used to decode the pixel
|
||||||
|
// data. If one hGroup is used for the entire image, then hPix and hBits will
|
||||||
|
// be zero. If more than one hGroup is used, then hPix contains the meta-image
|
||||||
|
// that maps tiles to hGroup index, and hBits contains the log-2 tile size.
|
||||||
|
func (d *decoder) decodeHuffmanGroups(w int32, h int32, topLevel bool, ccBits uint32) (
|
||||||
|
hGroups []hGroup, hPix []byte, hBits uint32, err error) {
|
||||||
|
|
||||||
|
maxHGroupIndex := 0
|
||||||
|
if topLevel {
|
||||||
|
useMeta, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
if useMeta != 0 {
|
||||||
|
hBits, err = d.read(3)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
hBits += 2
|
||||||
|
hPix, err = d.decodePix(nTiles(w, hBits), nTiles(h, hBits), 0, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
for p := 0; p < len(hPix); p += 4 {
|
||||||
|
i := int(hPix[p])<<8 | int(hPix[p+1])
|
||||||
|
if maxHGroupIndex < i {
|
||||||
|
maxHGroupIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hGroups = make([]hGroup, maxHGroupIndex+1)
|
||||||
|
for i := range hGroups {
|
||||||
|
for j, alphabetSize := range alphabetSizes {
|
||||||
|
if j == 0 && ccBits > 0 {
|
||||||
|
alphabetSize += 1 << ccBits
|
||||||
|
}
|
||||||
|
if err := d.decodeHuffmanTree(&hGroups[i][j], alphabetSize); err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hGroups, hPix, hBits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
nLiteralCodes = 256
|
||||||
|
nLengthCodes = 24
|
||||||
|
nDistanceCodes = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
var alphabetSizes = [nHuff]uint32{
|
||||||
|
nLiteralCodes + nLengthCodes,
|
||||||
|
nLiteralCodes,
|
||||||
|
nLiteralCodes,
|
||||||
|
nLiteralCodes,
|
||||||
|
nDistanceCodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePix decodes pixel data, specified in section 5.2.2.
|
||||||
|
func (d *decoder) decodePix(w int32, h int32, minCap int32, topLevel bool) ([]byte, error) {
|
||||||
|
// Decode the color cache parameters.
|
||||||
|
ccBits, ccShift, ccEntries := uint32(0), uint32(0), ([]uint32)(nil)
|
||||||
|
useColorCache, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if useColorCache != 0 {
|
||||||
|
ccBits, err = d.read(4)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ccBits < 1 || 11 < ccBits {
|
||||||
|
return nil, errors.New("vp8l: invalid color cache parameters")
|
||||||
|
}
|
||||||
|
ccShift = 32 - ccBits
|
||||||
|
ccEntries = make([]uint32, 1<<ccBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the Huffman groups.
|
||||||
|
hGroups, hPix, hBits, err := d.decodeHuffmanGroups(w, h, topLevel, ccBits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hMask, tilesPerRow := int32(0), int32(0)
|
||||||
|
if hBits != 0 {
|
||||||
|
hMask, tilesPerRow = 1<<hBits-1, nTiles(w, hBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the pixels.
|
||||||
|
if minCap < 4*w*h {
|
||||||
|
minCap = 4 * w * h
|
||||||
|
}
|
||||||
|
pix := make([]byte, 4*w*h, minCap)
|
||||||
|
p, cachedP := 0, 0
|
||||||
|
x, y := int32(0), int32(0)
|
||||||
|
hg, lookupHG := &hGroups[0], hMask != 0
|
||||||
|
for p < len(pix) {
|
||||||
|
if lookupHG {
|
||||||
|
i := 4 * (tilesPerRow*(y>>hBits) + (x >> hBits))
|
||||||
|
hg = &hGroups[uint32(hPix[i])<<8|uint32(hPix[i+1])]
|
||||||
|
}
|
||||||
|
|
||||||
|
green, err := hg[huffGreen].next(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case green < nLiteralCodes:
|
||||||
|
// We have a literal pixel.
|
||||||
|
red, err := hg[huffRed].next(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blue, err := hg[huffBlue].next(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
alpha, err := hg[huffAlpha].next(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pix[p+0] = uint8(red)
|
||||||
|
pix[p+1] = uint8(green)
|
||||||
|
pix[p+2] = uint8(blue)
|
||||||
|
pix[p+3] = uint8(alpha)
|
||||||
|
p += 4
|
||||||
|
|
||||||
|
x++
|
||||||
|
if x == w {
|
||||||
|
x, y = 0, y+1
|
||||||
|
}
|
||||||
|
lookupHG = hMask != 0 && x&hMask == 0
|
||||||
|
|
||||||
|
case green < nLiteralCodes+nLengthCodes:
|
||||||
|
// We have a LZ77 backwards reference.
|
||||||
|
length, err := d.lz77Param(green - nLiteralCodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
distSym, err := hg[huffDistance].next(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
distCode, err := d.lz77Param(distSym)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dist := distanceMap(w, distCode)
|
||||||
|
pEnd := p + 4*int(length)
|
||||||
|
q := p - 4*int(dist)
|
||||||
|
qEnd := pEnd - 4*int(dist)
|
||||||
|
if p < 0 || len(pix) < pEnd || q < 0 || len(pix) < qEnd {
|
||||||
|
return nil, errors.New("vp8l: invalid LZ77 parameters")
|
||||||
|
}
|
||||||
|
for ; p < pEnd; p, q = p+1, q+1 {
|
||||||
|
pix[p] = pix[q]
|
||||||
|
}
|
||||||
|
|
||||||
|
x += int32(length)
|
||||||
|
for x >= w {
|
||||||
|
x, y = x-w, y+1
|
||||||
|
}
|
||||||
|
lookupHG = hMask != 0
|
||||||
|
|
||||||
|
default:
|
||||||
|
// We have a color cache lookup. First, insert previous pixels
|
||||||
|
// into the cache. Note that VP8L assumes ARGB order, but the
|
||||||
|
// Go image.RGBA type is in RGBA order.
|
||||||
|
for ; cachedP < p; cachedP += 4 {
|
||||||
|
argb := uint32(pix[cachedP+0])<<16 |
|
||||||
|
uint32(pix[cachedP+1])<<8 |
|
||||||
|
uint32(pix[cachedP+2])<<0 |
|
||||||
|
uint32(pix[cachedP+3])<<24
|
||||||
|
ccEntries[(argb*colorCacheMultiplier)>>ccShift] = argb
|
||||||
|
}
|
||||||
|
green -= nLiteralCodes + nLengthCodes
|
||||||
|
if int(green) >= len(ccEntries) {
|
||||||
|
return nil, errors.New("vp8l: invalid color cache index")
|
||||||
|
}
|
||||||
|
argb := ccEntries[green]
|
||||||
|
pix[p+0] = uint8(argb >> 16)
|
||||||
|
pix[p+1] = uint8(argb >> 8)
|
||||||
|
pix[p+2] = uint8(argb >> 0)
|
||||||
|
pix[p+3] = uint8(argb >> 24)
|
||||||
|
p += 4
|
||||||
|
|
||||||
|
x++
|
||||||
|
if x == w {
|
||||||
|
x, y = 0, y+1
|
||||||
|
}
|
||||||
|
lookupHG = hMask != 0 && x&hMask == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pix, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// lz77Param returns the next LZ77 parameter: a length or a distance, specified
|
||||||
|
// in section 4.2.2.
|
||||||
|
func (d *decoder) lz77Param(symbol uint32) (uint32, error) {
|
||||||
|
if symbol < 4 {
|
||||||
|
return symbol + 1, nil
|
||||||
|
}
|
||||||
|
extraBits := (symbol - 2) >> 1
|
||||||
|
offset := (2 + symbol&1) << extraBits
|
||||||
|
n, err := d.read(extraBits)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return offset + n + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeHeader decodes the VP8L header from r.
|
||||||
|
func decodeHeader(r io.Reader) (d *decoder, w int32, h int32, err error) {
|
||||||
|
rr, ok := r.(io.ByteReader)
|
||||||
|
if !ok {
|
||||||
|
rr = bufio.NewReader(r)
|
||||||
|
}
|
||||||
|
d = &decoder{r: rr}
|
||||||
|
magic, err := d.read(8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
if magic != 0x2f {
|
||||||
|
return nil, 0, 0, errors.New("vp8l: invalid header")
|
||||||
|
}
|
||||||
|
width, err := d.read(14)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
width++
|
||||||
|
height, err := d.read(14)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
height++
|
||||||
|
_, err = d.read(1) // Read and ignore the hasAlpha hint.
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
version, err := d.read(3)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
if version != 0 {
|
||||||
|
return nil, 0, 0, errors.New("vp8l: invalid version")
|
||||||
|
}
|
||||||
|
return d, int32(width), int32(height), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeConfig decodes the color model and dimensions of a VP8L image from r.
|
||||||
|
func DecodeConfig(r io.Reader) (image.Config, error) {
|
||||||
|
_, w, h, err := decodeHeader(r)
|
||||||
|
if err != nil {
|
||||||
|
return image.Config{}, err
|
||||||
|
}
|
||||||
|
return image.Config{
|
||||||
|
ColorModel: color.NRGBAModel,
|
||||||
|
Width: int(w),
|
||||||
|
Height: int(h),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes a VP8L image from r.
|
||||||
|
func Decode(r io.Reader) (image.Image, error) {
|
||||||
|
d, w, h, err := decodeHeader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Decode the transforms.
|
||||||
|
var (
|
||||||
|
nTransforms int
|
||||||
|
transforms [nTransformTypes]transform
|
||||||
|
transformsSeen [nTransformTypes]bool
|
||||||
|
originalW = w
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
more, err := d.read(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if more == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var t transform
|
||||||
|
t, w, err = d.decodeTransform(w, h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if transformsSeen[t.transformType] {
|
||||||
|
return nil, errors.New("vp8l: repeated transform")
|
||||||
|
}
|
||||||
|
transformsSeen[t.transformType] = true
|
||||||
|
transforms[nTransforms] = t
|
||||||
|
nTransforms++
|
||||||
|
}
|
||||||
|
// Decode the transformed pixels.
|
||||||
|
pix, err := d.decodePix(w, h, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Apply the inverse transformations.
|
||||||
|
for i := nTransforms - 1; i >= 0; i-- {
|
||||||
|
t := &transforms[i]
|
||||||
|
pix = inverseTransforms[t.transformType](t, pix, h)
|
||||||
|
}
|
||||||
|
return &image.NRGBA{
|
||||||
|
Pix: pix,
|
||||||
|
Stride: 4 * int(originalW),
|
||||||
|
Rect: image.Rect(0, 0, int(originalW), int(h)),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
245
vendor/golang.org/x/image/vp8l/huffman.go
generated
vendored
Normal file
245
vendor/golang.org/x/image/vp8l/huffman.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8l
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// reverseBits reverses the bits in a byte.
|
||||||
|
var reverseBits = [256]uint8{
|
||||||
|
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||||||
|
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||||||
|
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||||||
|
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||||||
|
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||||||
|
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||||||
|
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||||||
|
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||||||
|
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||||||
|
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||||||
|
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||||||
|
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||||||
|
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||||||
|
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||||||
|
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||||||
|
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||||||
|
}
|
||||||
|
|
||||||
|
// hNode is a node in a Huffman tree.
|
||||||
|
type hNode struct {
|
||||||
|
// symbol is the symbol held by this node.
|
||||||
|
symbol uint32
|
||||||
|
// children, if positive, is the hTree.nodes index of the first of
|
||||||
|
// this node's two children. Zero means an uninitialized node,
|
||||||
|
// and -1 means a leaf node.
|
||||||
|
children int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const leafNode = -1
|
||||||
|
|
||||||
|
// lutSize is the log-2 size of an hTree's look-up table.
|
||||||
|
const lutSize, lutMask = 7, 1<<7 - 1
|
||||||
|
|
||||||
|
// hTree is a Huffman tree.
|
||||||
|
type hTree struct {
|
||||||
|
// nodes are the nodes of the Huffman tree. During construction,
|
||||||
|
// len(nodes) grows from 1 up to cap(nodes) by steps of two.
|
||||||
|
// After construction, len(nodes) == cap(nodes), and both equal
|
||||||
|
// 2*theNumberOfSymbols - 1.
|
||||||
|
nodes []hNode
|
||||||
|
// lut is a look-up table for walking the nodes. The x in lut[x] is
|
||||||
|
// the next lutSize bits in the bit-stream. The low 8 bits of lut[x]
|
||||||
|
// equals 1 plus the number of bits in the next code, or 0 if the
|
||||||
|
// next code requires more than lutSize bits. The high 24 bits are:
|
||||||
|
// - the symbol, if the code requires lutSize or fewer bits, or
|
||||||
|
// - the hTree.nodes index to start the tree traversal from, if
|
||||||
|
// the next code requires more than lutSize bits.
|
||||||
|
lut [1 << lutSize]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert inserts into the hTree a symbol whose encoding is the least
|
||||||
|
// significant codeLength bits of code.
|
||||||
|
func (h *hTree) insert(symbol uint32, code uint32, codeLength uint32) error {
|
||||||
|
if symbol > 0xffff || codeLength > 0xfe {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
baseCode := uint32(0)
|
||||||
|
if codeLength > lutSize {
|
||||||
|
baseCode = uint32(reverseBits[(code>>(codeLength-lutSize))&0xff]) >> (8 - lutSize)
|
||||||
|
} else {
|
||||||
|
baseCode = uint32(reverseBits[code&0xff]) >> (8 - codeLength)
|
||||||
|
for i := 0; i < 1<<(lutSize-codeLength); i++ {
|
||||||
|
h.lut[baseCode|uint32(i)<<codeLength] = symbol<<8 | (codeLength + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := uint32(0)
|
||||||
|
for jump := lutSize; codeLength > 0; {
|
||||||
|
codeLength--
|
||||||
|
if int(n) > len(h.nodes) {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
switch h.nodes[n].children {
|
||||||
|
case leafNode:
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
case 0:
|
||||||
|
if len(h.nodes) == cap(h.nodes) {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
// Create two empty child nodes.
|
||||||
|
h.nodes[n].children = int32(len(h.nodes))
|
||||||
|
h.nodes = h.nodes[:len(h.nodes)+2]
|
||||||
|
}
|
||||||
|
n = uint32(h.nodes[n].children) + 1&(code>>codeLength)
|
||||||
|
jump--
|
||||||
|
if jump == 0 && h.lut[baseCode] == 0 {
|
||||||
|
h.lut[baseCode] = n << 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch h.nodes[n].children {
|
||||||
|
case leafNode:
|
||||||
|
// No-op.
|
||||||
|
case 0:
|
||||||
|
// Turn the uninitialized node into a leaf.
|
||||||
|
h.nodes[n].children = leafNode
|
||||||
|
default:
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
h.nodes[n].symbol = symbol
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// codeLengthsToCodes returns the canonical Huffman codes implied by the
|
||||||
|
// sequence of code lengths.
|
||||||
|
func codeLengthsToCodes(codeLengths []uint32) ([]uint32, error) {
|
||||||
|
maxCodeLength := uint32(0)
|
||||||
|
for _, cl := range codeLengths {
|
||||||
|
if maxCodeLength < cl {
|
||||||
|
maxCodeLength = cl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const maxAllowedCodeLength = 15
|
||||||
|
if len(codeLengths) == 0 || maxCodeLength > maxAllowedCodeLength {
|
||||||
|
return nil, errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
histogram := [maxAllowedCodeLength + 1]uint32{}
|
||||||
|
for _, cl := range codeLengths {
|
||||||
|
histogram[cl]++
|
||||||
|
}
|
||||||
|
currCode, nextCodes := uint32(0), [maxAllowedCodeLength + 1]uint32{}
|
||||||
|
for cl := 1; cl < len(nextCodes); cl++ {
|
||||||
|
currCode = (currCode + histogram[cl-1]) << 1
|
||||||
|
nextCodes[cl] = currCode
|
||||||
|
}
|
||||||
|
codes := make([]uint32, len(codeLengths))
|
||||||
|
for symbol, cl := range codeLengths {
|
||||||
|
if cl > 0 {
|
||||||
|
codes[symbol] = nextCodes[cl]
|
||||||
|
nextCodes[cl]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return codes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// build builds a canonical Huffman tree from the given code lengths.
|
||||||
|
func (h *hTree) build(codeLengths []uint32) error {
|
||||||
|
// Calculate the number of symbols.
|
||||||
|
var nSymbols, lastSymbol uint32
|
||||||
|
for symbol, cl := range codeLengths {
|
||||||
|
if cl != 0 {
|
||||||
|
nSymbols++
|
||||||
|
lastSymbol = uint32(symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nSymbols == 0 {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
h.nodes = make([]hNode, 1, 2*nSymbols-1)
|
||||||
|
// Handle the trivial case.
|
||||||
|
if nSymbols == 1 {
|
||||||
|
if len(codeLengths) <= int(lastSymbol) {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
return h.insert(lastSymbol, 0, 0)
|
||||||
|
}
|
||||||
|
// Handle the non-trivial case.
|
||||||
|
codes, err := codeLengthsToCodes(codeLengths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for symbol, cl := range codeLengths {
|
||||||
|
if cl > 0 {
|
||||||
|
if err := h.insert(uint32(symbol), codes[symbol], cl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildSimple builds a Huffman tree with 1 or 2 symbols.
|
||||||
|
func (h *hTree) buildSimple(nSymbols uint32, symbols [2]uint32, alphabetSize uint32) error {
|
||||||
|
h.nodes = make([]hNode, 1, 2*nSymbols-1)
|
||||||
|
for i := uint32(0); i < nSymbols; i++ {
|
||||||
|
if symbols[i] >= alphabetSize {
|
||||||
|
return errInvalidHuffmanTree
|
||||||
|
}
|
||||||
|
if err := h.insert(symbols[i], i, nSymbols-1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// next returns the next Huffman-encoded symbol from the bit-stream d.
|
||||||
|
func (h *hTree) next(d *decoder) (uint32, error) {
|
||||||
|
var n uint32
|
||||||
|
// Read enough bits so that we can use the look-up table.
|
||||||
|
if d.nBits < lutSize {
|
||||||
|
c, err := d.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
// There are no more bytes of data, but we may still be able
|
||||||
|
// to read the next symbol out of the previously read bits.
|
||||||
|
goto slowPath
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d.bits |= uint32(c) << d.nBits
|
||||||
|
d.nBits += 8
|
||||||
|
}
|
||||||
|
// Use the look-up table.
|
||||||
|
n = h.lut[d.bits&lutMask]
|
||||||
|
if b := n & 0xff; b != 0 {
|
||||||
|
b--
|
||||||
|
d.bits >>= b
|
||||||
|
d.nBits -= b
|
||||||
|
return n >> 8, nil
|
||||||
|
}
|
||||||
|
n >>= 8
|
||||||
|
d.bits >>= lutSize
|
||||||
|
d.nBits -= lutSize
|
||||||
|
|
||||||
|
slowPath:
|
||||||
|
for h.nodes[n].children != leafNode {
|
||||||
|
if d.nBits == 0 {
|
||||||
|
c, err := d.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d.bits = uint32(c)
|
||||||
|
d.nBits = 8
|
||||||
|
}
|
||||||
|
n = uint32(h.nodes[n].children) + 1&d.bits
|
||||||
|
d.bits >>= 1
|
||||||
|
d.nBits--
|
||||||
|
}
|
||||||
|
return h.nodes[n].symbol, nil
|
||||||
|
}
|
||||||
299
vendor/golang.org/x/image/vp8l/transform.go
generated
vendored
Normal file
299
vendor/golang.org/x/image/vp8l/transform.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package vp8l
|
||||||
|
|
||||||
|
// This file deals with image transforms, specified in section 3.
|
||||||
|
|
||||||
|
// nTiles returns the number of tiles needed to cover size pixels, where each
|
||||||
|
// tile's side is 1<<bits pixels long.
|
||||||
|
func nTiles(size int32, bits uint32) int32 {
|
||||||
|
return (size + 1<<bits - 1) >> bits
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
transformTypePredictor = 0
|
||||||
|
transformTypeCrossColor = 1
|
||||||
|
transformTypeSubtractGreen = 2
|
||||||
|
transformTypeColorIndexing = 3
|
||||||
|
nTransformTypes = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// transform holds the parameters for an invertible transform.
|
||||||
|
type transform struct {
|
||||||
|
// transformType is the type of the transform.
|
||||||
|
transformType uint32
|
||||||
|
// oldWidth is the width of the image before transformation (or
|
||||||
|
// equivalently, after inverse transformation). The color-indexing
|
||||||
|
// transform can reduce the width. For example, a 50-pixel-wide
|
||||||
|
// image that only needs 4 bits (half a byte) per color index can
|
||||||
|
// be transformed into a 25-pixel-wide image.
|
||||||
|
oldWidth int32
|
||||||
|
// bits is the log-2 size of the transform's tiles, for the predictor
|
||||||
|
// and cross-color transforms. 8>>bits is the number of bits per
|
||||||
|
// color index, for the color-index transform.
|
||||||
|
bits uint32
|
||||||
|
// pix is the tile values, for the predictor and cross-color
|
||||||
|
// transforms, and the color palette, for the color-index transform.
|
||||||
|
pix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var inverseTransforms = [nTransformTypes]func(*transform, []byte, int32) []byte{
|
||||||
|
transformTypePredictor: inversePredictor,
|
||||||
|
transformTypeCrossColor: inverseCrossColor,
|
||||||
|
transformTypeSubtractGreen: inverseSubtractGreen,
|
||||||
|
transformTypeColorIndexing: inverseColorIndexing,
|
||||||
|
}
|
||||||
|
|
||||||
|
func inversePredictor(t *transform, pix []byte, h int32) []byte {
|
||||||
|
if t.oldWidth == 0 || h == 0 {
|
||||||
|
return pix
|
||||||
|
}
|
||||||
|
// The first pixel's predictor is mode 0 (opaque black).
|
||||||
|
pix[3] += 0xff
|
||||||
|
p, mask := int32(4), int32(1)<<t.bits-1
|
||||||
|
for x := int32(1); x < t.oldWidth; x++ {
|
||||||
|
// The rest of the first row's predictor is mode 1 (L).
|
||||||
|
pix[p+0] += pix[p-4]
|
||||||
|
pix[p+1] += pix[p-3]
|
||||||
|
pix[p+2] += pix[p-2]
|
||||||
|
pix[p+3] += pix[p-1]
|
||||||
|
p += 4
|
||||||
|
}
|
||||||
|
top, tilesPerRow := 0, nTiles(t.oldWidth, t.bits)
|
||||||
|
for y := int32(1); y < h; y++ {
|
||||||
|
// The first column's predictor is mode 2 (T).
|
||||||
|
pix[p+0] += pix[top+0]
|
||||||
|
pix[p+1] += pix[top+1]
|
||||||
|
pix[p+2] += pix[top+2]
|
||||||
|
pix[p+3] += pix[top+3]
|
||||||
|
p, top = p+4, top+4
|
||||||
|
|
||||||
|
q := 4 * (y >> t.bits) * tilesPerRow
|
||||||
|
predictorMode := t.pix[q+1] & 0x0f
|
||||||
|
q += 4
|
||||||
|
for x := int32(1); x < t.oldWidth; x++ {
|
||||||
|
if x&mask == 0 {
|
||||||
|
predictorMode = t.pix[q+1] & 0x0f
|
||||||
|
q += 4
|
||||||
|
}
|
||||||
|
switch predictorMode {
|
||||||
|
case 0: // Opaque black.
|
||||||
|
pix[p+3] += 0xff
|
||||||
|
|
||||||
|
case 1: // L.
|
||||||
|
pix[p+0] += pix[p-4]
|
||||||
|
pix[p+1] += pix[p-3]
|
||||||
|
pix[p+2] += pix[p-2]
|
||||||
|
pix[p+3] += pix[p-1]
|
||||||
|
|
||||||
|
case 2: // T.
|
||||||
|
pix[p+0] += pix[top+0]
|
||||||
|
pix[p+1] += pix[top+1]
|
||||||
|
pix[p+2] += pix[top+2]
|
||||||
|
pix[p+3] += pix[top+3]
|
||||||
|
|
||||||
|
case 3: // TR.
|
||||||
|
pix[p+0] += pix[top+4]
|
||||||
|
pix[p+1] += pix[top+5]
|
||||||
|
pix[p+2] += pix[top+6]
|
||||||
|
pix[p+3] += pix[top+7]
|
||||||
|
|
||||||
|
case 4: // TL.
|
||||||
|
pix[p+0] += pix[top-4]
|
||||||
|
pix[p+1] += pix[top-3]
|
||||||
|
pix[p+2] += pix[top-2]
|
||||||
|
pix[p+3] += pix[top-1]
|
||||||
|
|
||||||
|
case 5: // Average2(Average2(L, TR), T).
|
||||||
|
pix[p+0] += avg2(avg2(pix[p-4], pix[top+4]), pix[top+0])
|
||||||
|
pix[p+1] += avg2(avg2(pix[p-3], pix[top+5]), pix[top+1])
|
||||||
|
pix[p+2] += avg2(avg2(pix[p-2], pix[top+6]), pix[top+2])
|
||||||
|
pix[p+3] += avg2(avg2(pix[p-1], pix[top+7]), pix[top+3])
|
||||||
|
|
||||||
|
case 6: // Average2(L, TL).
|
||||||
|
pix[p+0] += avg2(pix[p-4], pix[top-4])
|
||||||
|
pix[p+1] += avg2(pix[p-3], pix[top-3])
|
||||||
|
pix[p+2] += avg2(pix[p-2], pix[top-2])
|
||||||
|
pix[p+3] += avg2(pix[p-1], pix[top-1])
|
||||||
|
|
||||||
|
case 7: // Average2(L, T).
|
||||||
|
pix[p+0] += avg2(pix[p-4], pix[top+0])
|
||||||
|
pix[p+1] += avg2(pix[p-3], pix[top+1])
|
||||||
|
pix[p+2] += avg2(pix[p-2], pix[top+2])
|
||||||
|
pix[p+3] += avg2(pix[p-1], pix[top+3])
|
||||||
|
|
||||||
|
case 8: // Average2(TL, T).
|
||||||
|
pix[p+0] += avg2(pix[top-4], pix[top+0])
|
||||||
|
pix[p+1] += avg2(pix[top-3], pix[top+1])
|
||||||
|
pix[p+2] += avg2(pix[top-2], pix[top+2])
|
||||||
|
pix[p+3] += avg2(pix[top-1], pix[top+3])
|
||||||
|
|
||||||
|
case 9: // Average2(T, TR).
|
||||||
|
pix[p+0] += avg2(pix[top+0], pix[top+4])
|
||||||
|
pix[p+1] += avg2(pix[top+1], pix[top+5])
|
||||||
|
pix[p+2] += avg2(pix[top+2], pix[top+6])
|
||||||
|
pix[p+3] += avg2(pix[top+3], pix[top+7])
|
||||||
|
|
||||||
|
case 10: // Average2(Average2(L, TL), Average2(T, TR)).
|
||||||
|
pix[p+0] += avg2(avg2(pix[p-4], pix[top-4]), avg2(pix[top+0], pix[top+4]))
|
||||||
|
pix[p+1] += avg2(avg2(pix[p-3], pix[top-3]), avg2(pix[top+1], pix[top+5]))
|
||||||
|
pix[p+2] += avg2(avg2(pix[p-2], pix[top-2]), avg2(pix[top+2], pix[top+6]))
|
||||||
|
pix[p+3] += avg2(avg2(pix[p-1], pix[top-1]), avg2(pix[top+3], pix[top+7]))
|
||||||
|
|
||||||
|
case 11: // Select(L, T, TL).
|
||||||
|
l0 := int32(pix[p-4])
|
||||||
|
l1 := int32(pix[p-3])
|
||||||
|
l2 := int32(pix[p-2])
|
||||||
|
l3 := int32(pix[p-1])
|
||||||
|
c0 := int32(pix[top-4])
|
||||||
|
c1 := int32(pix[top-3])
|
||||||
|
c2 := int32(pix[top-2])
|
||||||
|
c3 := int32(pix[top-1])
|
||||||
|
t0 := int32(pix[top+0])
|
||||||
|
t1 := int32(pix[top+1])
|
||||||
|
t2 := int32(pix[top+2])
|
||||||
|
t3 := int32(pix[top+3])
|
||||||
|
l := abs(c0-t0) + abs(c1-t1) + abs(c2-t2) + abs(c3-t3)
|
||||||
|
t := abs(c0-l0) + abs(c1-l1) + abs(c2-l2) + abs(c3-l3)
|
||||||
|
if l < t {
|
||||||
|
pix[p+0] += uint8(l0)
|
||||||
|
pix[p+1] += uint8(l1)
|
||||||
|
pix[p+2] += uint8(l2)
|
||||||
|
pix[p+3] += uint8(l3)
|
||||||
|
} else {
|
||||||
|
pix[p+0] += uint8(t0)
|
||||||
|
pix[p+1] += uint8(t1)
|
||||||
|
pix[p+2] += uint8(t2)
|
||||||
|
pix[p+3] += uint8(t3)
|
||||||
|
}
|
||||||
|
|
||||||
|
case 12: // ClampAddSubtractFull(L, T, TL).
|
||||||
|
pix[p+0] += clampAddSubtractFull(pix[p-4], pix[top+0], pix[top-4])
|
||||||
|
pix[p+1] += clampAddSubtractFull(pix[p-3], pix[top+1], pix[top-3])
|
||||||
|
pix[p+2] += clampAddSubtractFull(pix[p-2], pix[top+2], pix[top-2])
|
||||||
|
pix[p+3] += clampAddSubtractFull(pix[p-1], pix[top+3], pix[top-1])
|
||||||
|
|
||||||
|
case 13: // ClampAddSubtractHalf(Average2(L, T), TL).
|
||||||
|
pix[p+0] += clampAddSubtractHalf(avg2(pix[p-4], pix[top+0]), pix[top-4])
|
||||||
|
pix[p+1] += clampAddSubtractHalf(avg2(pix[p-3], pix[top+1]), pix[top-3])
|
||||||
|
pix[p+2] += clampAddSubtractHalf(avg2(pix[p-2], pix[top+2]), pix[top-2])
|
||||||
|
pix[p+3] += clampAddSubtractHalf(avg2(pix[p-1], pix[top+3]), pix[top-1])
|
||||||
|
}
|
||||||
|
p, top = p+4, top+4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pix
|
||||||
|
}
|
||||||
|
|
||||||
|
func inverseCrossColor(t *transform, pix []byte, h int32) []byte {
|
||||||
|
var greenToRed, greenToBlue, redToBlue int32
|
||||||
|
p, mask, tilesPerRow := int32(0), int32(1)<<t.bits-1, nTiles(t.oldWidth, t.bits)
|
||||||
|
for y := int32(0); y < h; y++ {
|
||||||
|
q := 4 * (y >> t.bits) * tilesPerRow
|
||||||
|
for x := int32(0); x < t.oldWidth; x++ {
|
||||||
|
if x&mask == 0 {
|
||||||
|
redToBlue = int32(int8(t.pix[q+0]))
|
||||||
|
greenToBlue = int32(int8(t.pix[q+1]))
|
||||||
|
greenToRed = int32(int8(t.pix[q+2]))
|
||||||
|
q += 4
|
||||||
|
}
|
||||||
|
red := pix[p+0]
|
||||||
|
green := pix[p+1]
|
||||||
|
blue := pix[p+2]
|
||||||
|
red += uint8(uint32(greenToRed*int32(int8(green))) >> 5)
|
||||||
|
blue += uint8(uint32(greenToBlue*int32(int8(green))) >> 5)
|
||||||
|
blue += uint8(uint32(redToBlue*int32(int8(red))) >> 5)
|
||||||
|
pix[p+0] = red
|
||||||
|
pix[p+2] = blue
|
||||||
|
p += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pix
|
||||||
|
}
|
||||||
|
|
||||||
|
func inverseSubtractGreen(t *transform, pix []byte, h int32) []byte {
|
||||||
|
for p := 0; p < len(pix); p += 4 {
|
||||||
|
green := pix[p+1]
|
||||||
|
pix[p+0] += green
|
||||||
|
pix[p+2] += green
|
||||||
|
}
|
||||||
|
return pix
|
||||||
|
}
|
||||||
|
|
||||||
|
func inverseColorIndexing(t *transform, pix []byte, h int32) []byte {
|
||||||
|
if t.bits == 0 {
|
||||||
|
for p := 0; p < len(pix); p += 4 {
|
||||||
|
i := 4 * uint32(pix[p+1])
|
||||||
|
pix[p+0] = t.pix[i+0]
|
||||||
|
pix[p+1] = t.pix[i+1]
|
||||||
|
pix[p+2] = t.pix[i+2]
|
||||||
|
pix[p+3] = t.pix[i+3]
|
||||||
|
}
|
||||||
|
return pix
|
||||||
|
}
|
||||||
|
|
||||||
|
vMask, xMask, bitsPerPixel := uint32(0), int32(0), uint32(8>>t.bits)
|
||||||
|
switch t.bits {
|
||||||
|
case 1:
|
||||||
|
vMask, xMask = 0x0f, 0x01
|
||||||
|
case 2:
|
||||||
|
vMask, xMask = 0x03, 0x03
|
||||||
|
case 3:
|
||||||
|
vMask, xMask = 0x01, 0x07
|
||||||
|
}
|
||||||
|
|
||||||
|
d, p, v, dst := 0, 0, uint32(0), make([]byte, 4*t.oldWidth*h)
|
||||||
|
for y := int32(0); y < h; y++ {
|
||||||
|
for x := int32(0); x < t.oldWidth; x++ {
|
||||||
|
if x&xMask == 0 {
|
||||||
|
v = uint32(pix[p+1])
|
||||||
|
p += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 4 * (v & vMask)
|
||||||
|
dst[d+0] = t.pix[i+0]
|
||||||
|
dst[d+1] = t.pix[i+1]
|
||||||
|
dst[d+2] = t.pix[i+2]
|
||||||
|
dst[d+3] = t.pix[i+3]
|
||||||
|
d += 4
|
||||||
|
|
||||||
|
v >>= bitsPerPixel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(x int32) int32 {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func avg2(a, b uint8) uint8 {
|
||||||
|
return uint8((int32(a) + int32(b)) / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clampAddSubtractFull(a, b, c uint8) uint8 {
|
||||||
|
x := int32(a) + int32(b) - int32(c)
|
||||||
|
if x < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if x > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clampAddSubtractHalf(a, b uint8) uint8 {
|
||||||
|
x := int32(a) + (int32(a)-int32(b))/2
|
||||||
|
if x < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if x > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(x)
|
||||||
|
}
|
||||||
272
vendor/golang.org/x/image/webp/decode.go
generated
vendored
Normal file
272
vendor/golang.org/x/image/webp/decode.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.6
|
||||||
|
|
||||||
|
package webp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/image/riff"
|
||||||
|
"golang.org/x/image/vp8"
|
||||||
|
"golang.org/x/image/vp8l"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidFormat = errors.New("webp: invalid format")
|
||||||
|
|
||||||
|
var (
|
||||||
|
fccALPH = riff.FourCC{'A', 'L', 'P', 'H'}
|
||||||
|
fccVP8 = riff.FourCC{'V', 'P', '8', ' '}
|
||||||
|
fccVP8L = riff.FourCC{'V', 'P', '8', 'L'}
|
||||||
|
fccVP8X = riff.FourCC{'V', 'P', '8', 'X'}
|
||||||
|
fccWEBP = riff.FourCC{'W', 'E', 'B', 'P'}
|
||||||
|
)
|
||||||
|
|
||||||
|
func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) {
|
||||||
|
formType, riffReader, err := riff.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
if formType != fccWEBP {
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
alpha []byte
|
||||||
|
alphaStride int
|
||||||
|
wantAlpha bool
|
||||||
|
widthMinusOne uint32
|
||||||
|
heightMinusOne uint32
|
||||||
|
buf [10]byte
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
chunkID, chunkLen, chunkData, err := riffReader.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = errInvalidFormat
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch chunkID {
|
||||||
|
case fccALPH:
|
||||||
|
if !wantAlpha {
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
wantAlpha = false
|
||||||
|
// Read the Pre-processing | Filter | Compression byte.
|
||||||
|
if _, err := io.ReadFull(chunkData, buf[:1]); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = errInvalidFormat
|
||||||
|
}
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
alpha, alphaStride, err = readAlpha(chunkData, widthMinusOne, heightMinusOne, buf[0]&0x03)
|
||||||
|
if err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
unfilterAlpha(alpha, alphaStride, (buf[0]>>2)&0x03)
|
||||||
|
|
||||||
|
case fccVP8:
|
||||||
|
if wantAlpha || int32(chunkLen) < 0 {
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
d := vp8.NewDecoder()
|
||||||
|
d.Init(chunkData, int(chunkLen))
|
||||||
|
fh, err := d.DecodeFrameHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
if configOnly {
|
||||||
|
return nil, image.Config{
|
||||||
|
ColorModel: color.YCbCrModel,
|
||||||
|
Width: fh.Width,
|
||||||
|
Height: fh.Height,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
m, err := d.DecodeFrame()
|
||||||
|
if err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
if alpha != nil {
|
||||||
|
return &image.NYCbCrA{
|
||||||
|
YCbCr: *m,
|
||||||
|
A: alpha,
|
||||||
|
AStride: alphaStride,
|
||||||
|
}, image.Config{}, nil
|
||||||
|
}
|
||||||
|
return m, image.Config{}, nil
|
||||||
|
|
||||||
|
case fccVP8L:
|
||||||
|
if wantAlpha || alpha != nil {
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
if configOnly {
|
||||||
|
c, err := vp8l.DecodeConfig(chunkData)
|
||||||
|
return nil, c, err
|
||||||
|
}
|
||||||
|
m, err := vp8l.Decode(chunkData)
|
||||||
|
return m, image.Config{}, err
|
||||||
|
|
||||||
|
case fccVP8X:
|
||||||
|
if chunkLen != 10 {
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(chunkData, buf[:10]); err != nil {
|
||||||
|
return nil, image.Config{}, err
|
||||||
|
}
|
||||||
|
const (
|
||||||
|
animationBit = 1 << 1
|
||||||
|
xmpMetadataBit = 1 << 2
|
||||||
|
exifMetadataBit = 1 << 3
|
||||||
|
alphaBit = 1 << 4
|
||||||
|
iccProfileBit = 1 << 5
|
||||||
|
)
|
||||||
|
if buf[0] != alphaBit {
|
||||||
|
return nil, image.Config{}, errors.New("webp: non-Alpha VP8X is not implemented")
|
||||||
|
}
|
||||||
|
widthMinusOne = uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16
|
||||||
|
heightMinusOne = uint32(buf[7]) | uint32(buf[8])<<8 | uint32(buf[9])<<16
|
||||||
|
if configOnly {
|
||||||
|
return nil, image.Config{
|
||||||
|
ColorModel: color.NYCbCrAModel,
|
||||||
|
Width: int(widthMinusOne) + 1,
|
||||||
|
Height: int(heightMinusOne) + 1,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
wantAlpha = true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, image.Config{}, errInvalidFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAlpha(chunkData io.Reader, widthMinusOne, heightMinusOne uint32, compression byte) (
|
||||||
|
alpha []byte, alphaStride int, err error) {
|
||||||
|
|
||||||
|
switch compression {
|
||||||
|
case 0:
|
||||||
|
w := int(widthMinusOne) + 1
|
||||||
|
h := int(heightMinusOne) + 1
|
||||||
|
alpha = make([]byte, w*h)
|
||||||
|
if _, err := io.ReadFull(chunkData, alpha); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return alpha, w, nil
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// Read the VP8L-compressed alpha values. First, synthesize a 5-byte VP8L header:
|
||||||
|
// a 1-byte magic number, a 14-bit widthMinusOne, a 14-bit heightMinusOne,
|
||||||
|
// a 1-bit (ignored, zero) alphaIsUsed and a 3-bit (zero) version.
|
||||||
|
// TODO(nigeltao): be more efficient than decoding an *image.NRGBA just to
|
||||||
|
// extract the green values to a separately allocated []byte. Fixing this
|
||||||
|
// will require changes to the vp8l package's API.
|
||||||
|
if widthMinusOne > 0x3fff || heightMinusOne > 0x3fff {
|
||||||
|
return nil, 0, errors.New("webp: invalid format")
|
||||||
|
}
|
||||||
|
alphaImage, err := vp8l.Decode(io.MultiReader(
|
||||||
|
bytes.NewReader([]byte{
|
||||||
|
0x2f, // VP8L magic number.
|
||||||
|
uint8(widthMinusOne),
|
||||||
|
uint8(widthMinusOne>>8) | uint8(heightMinusOne<<6),
|
||||||
|
uint8(heightMinusOne >> 2),
|
||||||
|
uint8(heightMinusOne >> 10),
|
||||||
|
}),
|
||||||
|
chunkData,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
// The green values of the inner NRGBA image are the alpha values of the
|
||||||
|
// outer NYCbCrA image.
|
||||||
|
pix := alphaImage.(*image.NRGBA).Pix
|
||||||
|
alpha = make([]byte, len(pix)/4)
|
||||||
|
for i := range alpha {
|
||||||
|
alpha[i] = pix[4*i+1]
|
||||||
|
}
|
||||||
|
return alpha, int(widthMinusOne) + 1, nil
|
||||||
|
}
|
||||||
|
return nil, 0, errInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
func unfilterAlpha(alpha []byte, alphaStride int, filter byte) {
|
||||||
|
if len(alpha) == 0 || alphaStride == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch filter {
|
||||||
|
case 1: // Horizontal filter.
|
||||||
|
for i := 1; i < alphaStride; i++ {
|
||||||
|
alpha[i] += alpha[i-1]
|
||||||
|
}
|
||||||
|
for i := alphaStride; i < len(alpha); i += alphaStride {
|
||||||
|
// The first column is equivalent to the vertical filter.
|
||||||
|
alpha[i] += alpha[i-alphaStride]
|
||||||
|
|
||||||
|
for j := 1; j < alphaStride; j++ {
|
||||||
|
alpha[i+j] += alpha[i+j-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: // Vertical filter.
|
||||||
|
// The first row is equivalent to the horizontal filter.
|
||||||
|
for i := 1; i < alphaStride; i++ {
|
||||||
|
alpha[i] += alpha[i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := alphaStride; i < len(alpha); i++ {
|
||||||
|
alpha[i] += alpha[i-alphaStride]
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3: // Gradient filter.
|
||||||
|
// The first row is equivalent to the horizontal filter.
|
||||||
|
for i := 1; i < alphaStride; i++ {
|
||||||
|
alpha[i] += alpha[i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := alphaStride; i < len(alpha); i += alphaStride {
|
||||||
|
// The first column is equivalent to the vertical filter.
|
||||||
|
alpha[i] += alpha[i-alphaStride]
|
||||||
|
|
||||||
|
// The interior is predicted on the three top/left pixels.
|
||||||
|
for j := 1; j < alphaStride; j++ {
|
||||||
|
c := int(alpha[i+j-alphaStride-1])
|
||||||
|
b := int(alpha[i+j-alphaStride])
|
||||||
|
a := int(alpha[i+j-1])
|
||||||
|
x := a + b - c
|
||||||
|
if x < 0 {
|
||||||
|
x = 0
|
||||||
|
} else if x > 255 {
|
||||||
|
x = 255
|
||||||
|
}
|
||||||
|
alpha[i+j] += uint8(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads a WEBP image from r and returns it as an image.Image.
|
||||||
|
func Decode(r io.Reader) (image.Image, error) {
|
||||||
|
m, _, err := decode(r, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeConfig returns the color model and dimensions of a WEBP image without
|
||||||
|
// decoding the entire image.
|
||||||
|
func DecodeConfig(r io.Reader) (image.Config, error) {
|
||||||
|
_, c, err := decode(r, true)
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
image.RegisterFormat("webp", "RIFF????WEBPVP8", Decode, DecodeConfig)
|
||||||
|
}
|
||||||
30
vendor/golang.org/x/image/webp/webp.go
generated
vendored
Normal file
30
vendor/golang.org/x/image/webp/webp.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package webp implements a decoder for WEBP images.
|
||||||
|
//
|
||||||
|
// WEBP is defined at:
|
||||||
|
// https://developers.google.com/speed/webp/docs/riff_container
|
||||||
|
//
|
||||||
|
// It requires Go 1.6 or later.
|
||||||
|
package webp // import "golang.org/x/image/webp"
|
||||||
|
|
||||||
|
// This blank Go file, other than the package clause, exists so that this
|
||||||
|
// package can be built for Go 1.5 and earlier. (The other files in this
|
||||||
|
// package are all marked "+build go1.6" for the NYCbCrA types introduced in Go
|
||||||
|
// 1.6). There is no functionality in a blank package, but some image
|
||||||
|
// manipulation programs might still underscore import this package for the
|
||||||
|
// side effect of registering the WEBP format with the standard library's
|
||||||
|
// image.RegisterFormat and image.Decode functions. For example, that program
|
||||||
|
// might contain:
|
||||||
|
//
|
||||||
|
// // Underscore imports to register some formats for image.Decode.
|
||||||
|
// import _ "image/gif"
|
||||||
|
// import _ "image/jpeg"
|
||||||
|
// import _ "image/png"
|
||||||
|
// import _ "golang.org/x/image/webp"
|
||||||
|
//
|
||||||
|
// Such a program will still compile for Go 1.5 (due to this placeholder Go
|
||||||
|
// file). It will simply not be able to recognize and decode WEBP (but still
|
||||||
|
// handle GIF, JPEG and PNG).
|
||||||
13
vendor/modules.txt
vendored
Normal file
13
vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# code.ivysaur.me/imagequant v2.12.2-go1.2+incompatible
|
||||||
|
code.ivysaur.me/imagequant
|
||||||
|
# github.com/hashicorp/golang-lru v0.5.0
|
||||||
|
github.com/hashicorp/golang-lru
|
||||||
|
github.com/hashicorp/golang-lru/simplelru
|
||||||
|
# golang.org/x/image v0.0.0-20180601115456-af66defab954
|
||||||
|
golang.org/x/image/bmp
|
||||||
|
golang.org/x/image/draw
|
||||||
|
golang.org/x/image/webp
|
||||||
|
golang.org/x/image/math/f64
|
||||||
|
golang.org/x/image/riff
|
||||||
|
golang.org/x/image/vp8
|
||||||
|
golang.org/x/image/vp8l
|
||||||
67
video.go
67
video.go
@@ -1,67 +0,0 @@
|
|||||||
package thumbnail
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderScaledFfmpeg(absPath string) ([]byte, error) {
|
|
||||||
|
|
||||||
scaleCmd := fmt.Sprintf(
|
|
||||||
`thumbnail,scale='if(gt(a,%d/%d),%d,-1)':'if(gt(a,%d/%d),-1,%d)'`,
|
|
||||||
this.height, this.width, this.height,
|
|
||||||
this.height, this.width, this.width,
|
|
||||||
)
|
|
||||||
|
|
||||||
// BUG(): always produces ASPECT_SVELTE shape - need to re-pad for ASPECT_PADDED
|
|
||||||
|
|
||||||
var vcodec string
|
|
||||||
switch this.ofmt {
|
|
||||||
case OUTPUT_JPG:
|
|
||||||
vcodec = "mjpeg" // yes really
|
|
||||||
case OUTPUT_PNG_CRUSH:
|
|
||||||
vcodec = "png"
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidOption
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(
|
|
||||||
"ffmpeg",
|
|
||||||
"-loglevel", "0",
|
|
||||||
"-timelimit", "10", // seconds
|
|
||||||
"-an",
|
|
||||||
"-i", absPath,
|
|
||||||
"-vf", scaleCmd,
|
|
||||||
"-frames:v", "1",
|
|
||||||
"-f", "image2pipe",
|
|
||||||
"-c:v", vcodec,
|
|
||||||
"-",
|
|
||||||
)
|
|
||||||
|
|
||||||
// -ss 00:00:30
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out := bytes.Buffer{}
|
|
||||||
_, err = io.Copy(&out, stdout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Wait()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.Bytes(), nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user