initial commit

This commit is contained in:
harukasan 2015-01-09 12:51:34 +09:00
commit 5af6d93b3b
26 changed files with 1165 additions and 0 deletions

80
webp/cover.out Normal file
View file

@ -0,0 +1,80 @@
mode: count
github.com/harukasan/go-libwebp/webp/decode.go:41.34,43.2 1 1
github.com/harukasan/go-libwebp/webp/decode.go:46.47,50.2 3 1
github.com/harukasan/go-libwebp/webp/decode.go:53.65,57.37 3 1
github.com/harukasan/go-libwebp/webp/decode.go:61.2,69.8 2 1
github.com/harukasan/go-libwebp/webp/decode.go:57.37,59.13 1 0
github.com/harukasan/go-libwebp/webp/decode.go:73.83,75.16 2 2
github.com/harukasan/go-libwebp/webp/decode.go:79.2,83.107 3 2
github.com/harukasan/go-libwebp/webp/decode.go:87.2,96.42 6 2
github.com/harukasan/go-libwebp/webp/decode.go:100.2,114.32 14 2
github.com/harukasan/go-libwebp/webp/decode.go:120.2,120.95 1 2
github.com/harukasan/go-libwebp/webp/decode.go:124.2,124.8 1 2
github.com/harukasan/go-libwebp/webp/decode.go:75.16,77.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:83.107,85.13 1 0
github.com/harukasan/go-libwebp/webp/decode.go:96.42,98.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:114.32,118.3 3 0
github.com/harukasan/go-libwebp/webp/decode.go:120.95,122.13 1 0
github.com/harukasan/go-libwebp/webp/decode.go:128.84,130.16 2 3
github.com/harukasan/go-libwebp/webp/decode.go:134.2,138.107 3 3
github.com/harukasan/go-libwebp/webp/decode.go:143.2,157.95 9 3
github.com/harukasan/go-libwebp/webp/decode.go:161.2,161.8 1 3
github.com/harukasan/go-libwebp/webp/decode.go:130.16,132.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:138.107,140.13 1 0
github.com/harukasan/go-libwebp/webp/decode.go:157.95,159.13 1 0
github.com/harukasan/go-libwebp/webp/decode.go:165.55,166.16 1 0
github.com/harukasan/go-libwebp/webp/decode.go:184.2,184.33 1 0
github.com/harukasan/go-libwebp/webp/decode.go:167.2,168.25 1 0
github.com/harukasan/go-libwebp/webp/decode.go:169.2,170.36 1 0
github.com/harukasan/go-libwebp/webp/decode.go:171.2,172.36 1 0
github.com/harukasan/go-libwebp/webp/decode.go:173.2,174.38 1 0
github.com/harukasan/go-libwebp/webp/decode.go:175.2,176.42 1 0
github.com/harukasan/go-libwebp/webp/decode.go:177.2,178.32 1 0
github.com/harukasan/go-libwebp/webp/decode.go:179.2,180.33 1 0
github.com/harukasan/go-libwebp/webp/decode.go:181.2,182.38 1 0
github.com/harukasan/go-libwebp/webp/decode.go:188.102,191.57 2 5
github.com/harukasan/go-libwebp/webp/decode.go:196.2,196.29 1 5
github.com/harukasan/go-libwebp/webp/decode.go:199.2,199.31 1 5
github.com/harukasan/go-libwebp/webp/decode.go:202.2,202.54 1 5
github.com/harukasan/go-libwebp/webp/decode.go:209.2,209.56 1 5
github.com/harukasan/go-libwebp/webp/decode.go:214.2,214.24 1 5
github.com/harukasan/go-libwebp/webp/decode.go:217.12,219.8 2 5
github.com/harukasan/go-libwebp/webp/decode.go:191.57,193.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:196.29,198.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:199.31,201.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:202.54,208.3 5 1
github.com/harukasan/go-libwebp/webp/decode.go:209.56,213.3 3 1
github.com/harukasan/go-libwebp/webp/decode.go:214.24,216.3 1 0
github.com/harukasan/go-libwebp/webp/decode.go:223.82,225.29 2 5
github.com/harukasan/go-libwebp/webp/decode.go:230.2,230.37 1 4
github.com/harukasan/go-libwebp/webp/decode.go:236.2,238.18 3 3
github.com/harukasan/go-libwebp/webp/decode.go:225.29,229.3 3 1
github.com/harukasan/go-libwebp/webp/decode.go:230.37,234.3 3 1
github.com/harukasan/go-libwebp/webp/encode.go:42.101,46.16 4 11
github.com/harukasan/go-libwebp/webp/encode.go:49.2,49.10 1 11
github.com/harukasan/go-libwebp/webp/encode.go:46.16,48.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:53.69,55.16 2 1
github.com/harukasan/go-libwebp/webp/encode.go:59.2,60.49 2 1
github.com/harukasan/go-libwebp/webp/encode.go:63.12,73.46 8 1
github.com/harukasan/go-libwebp/webp/encode.go:77.2,77.8 1 1
github.com/harukasan/go-libwebp/webp/encode.go:55.16,57.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:60.49,62.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:73.46,75.13 1 0
github.com/harukasan/go-libwebp/webp/encode.go:81.68,83.16 2 1
github.com/harukasan/go-libwebp/webp/encode.go:87.2,88.49 2 1
github.com/harukasan/go-libwebp/webp/encode.go:91.12,101.31 10 1
github.com/harukasan/go-libwebp/webp/encode.go:106.2,109.46 3 1
github.com/harukasan/go-libwebp/webp/encode.go:113.2,113.8 1 1
github.com/harukasan/go-libwebp/webp/encode.go:83.16,85.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:88.49,90.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:101.31,104.3 2 0
github.com/harukasan/go-libwebp/webp/encode.go:109.46,111.13 1 0
github.com/harukasan/go-libwebp/webp/encode.go:117.73,119.96 2 2
github.com/harukasan/go-libwebp/webp/encode.go:122.12,125.44 3 2
github.com/harukasan/go-libwebp/webp/encode.go:128.2,128.8 1 2
github.com/harukasan/go-libwebp/webp/encode.go:119.96,121.3 1 0
github.com/harukasan/go-libwebp/webp/encode.go:125.44,127.3 1 0
github.com/harukasan/go-libwebp/webp/yuva_image.go:21.71,25.11 3 2
github.com/harukasan/go-libwebp/webp/yuva_image.go:55.2,55.8 1 2
github.com/harukasan/go-libwebp/webp/yuva_image.go:26.2,38.4 2 2
github.com/harukasan/go-libwebp/webp/yuva_image.go:40.2,52.4 2 0

242
webp/decode.go Normal file
View file

@ -0,0 +1,242 @@
package webp
/*
#cgo LDFLAGS: -lwebp
#include <stdlib.h>
#include <webp/decode.h>
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer);
*/
import "C"
import (
"errors"
"fmt"
"image"
"unsafe"
)
// DecoderOptions specifies decoding options
type DecoderOptions struct {
BypassFiltering bool
NoFancyUpsampling bool
Crop image.Rectangle
Scale image.Rectangle
UseThreads bool
DitheringStrength int
Flip bool
AlphaDitheringStrength int
}
// BitstreamFeatures retrived from data stream.
type BitstreamFeatures struct {
Width int // Image width in pixels
Height int // Image height in pixles
HasAlpha bool // True if data stream contains a alpha channel.
HasAnimation bool // True if data stream is an animation
Format int // Image compression format
NoIncrementalDecoding bool // True if incremental decording is not using
}
// GetDecoderVersion returns decoder's version number, packed in hexadecimal.
// e.g; v0.4.2 is 0x000402
func GetDecoderVersion() (v int) {
return int(C.WebPGetDecoderVersion())
}
// GetInfo retrives width/height from data bytes.
func GetInfo(data []byte) (width, height int) {
var w, h C.int
C.WebPGetInfo((*C.uint8_t)(&data[0]), (C.size_t)(len(data)), &w, &h)
return int(w), int(h)
}
// GetFeatures returns features retrived from data stream.
func GetFeatures(data []byte) (f *BitstreamFeatures, err error) {
var cf C.WebPBitstreamFeatures
status := C.WebPGetFeatures((*C.uint8_t)(&data[0]), (C.size_t)(len(data)), &cf)
if status != C.VP8_STATUS_OK {
return nil, fmt.Errorf("WebPGetFeatures returns unexpected status: %s", statusString(status))
}
f = &BitstreamFeatures{
Width: int(cf.width), // TODO: use Rectangle instaed?
Height: int(cf.height),
HasAlpha: cf.has_alpha > 0,
HasAnimation: cf.has_animation > 0,
Format: int(cf.format),
NoIncrementalDecoding: cf.no_incremental_decoding == 1,
}
return
}
// DecodeYUVA decodes WebP image into YUV image with alpha channel.
func DecodeYUVA(data []byte, options *DecoderOptions) (img *YUVAImage, err error) {
config, err := initDecoderConfig(options)
if err != nil {
return nil, err
}
cDataPtr := (*C.uint8_t)(&data[0])
cDataSize := (C.size_t)(len(data))
// Retrive WebP features from data stream
if status := C.WebPGetFeatures(cDataPtr, cDataSize, &config.input); status != C.VP8_STATUS_OK {
return nil, fmt.Errorf("Could not get features from the data stream, return %s", statusString(status))
}
outWidth, outHeight := calcOutputSize(config)
buf := (*C.WebPYUVABuffer)(unsafe.Pointer(&config.output.u[0]))
// Set up output configurations
colorSpace := YUV420
config.output.colorspace = C.MODE_YUV
if config.input.has_alpha > 0 {
colorSpace = YUV420A
config.output.colorspace = C.MODE_YUVA
}
config.output.is_external_memory = 1
// Allocate image and fill into buffer
img = NewYUVAImage(image.Rect(0, 0, outWidth, outHeight), colorSpace)
buf.y = (*C.uint8_t)(&img.Y[0])
buf.u = (*C.uint8_t)(&img.Cb[0])
buf.v = (*C.uint8_t)(&img.Cr[0])
buf.a = nil
buf.y_stride = C.int(img.YStride)
buf.u_stride = C.int(img.CStride)
buf.v_stride = C.int(img.CStride)
buf.a_stride = 0
buf.y_size = C.size_t(len(img.Y))
buf.u_size = C.size_t(len(img.Cb))
buf.v_size = C.size_t(len(img.Cr))
buf.a_size = 0
if config.input.has_alpha > 0 {
buf.a = (*C.uint8_t)(&img.A[0])
buf.a_stride = C.int(img.AStride)
buf.a_size = C.size_t(len(img.A))
}
if status := C.WebPDecode(cDataPtr, cDataSize, config); status != C.VP8_STATUS_OK {
return nil, fmt.Errorf("Could not decode data stream, return %s", statusString(status))
}
return
}
// DecodeRGBA decodes WebP image into RGBA image.
func DecodeRGBA(data []byte, options *DecoderOptions) (img *image.RGBA, err error) {
config, err := initDecoderConfig(options)
if err != nil {
return nil, err
}
cDataPtr := (*C.uint8_t)(&data[0])
cDataSize := (C.size_t)(len(data))
// Retrive WebP features
if status := C.WebPGetFeatures(cDataPtr, cDataSize, &config.input); status != C.VP8_STATUS_OK {
return nil, fmt.Errorf("Could not get features from the data stream, return %s", statusString(status))
}
// Allocate output image
outWidth, outHeight := calcOutputSize(config)
img = image.NewRGBA(image.Rect(0, 0, outWidth, outHeight))
// Set up output configurations
config.output.colorspace = C.MODE_RGBA
config.output.is_external_memory = 1
// Allocate WebPRGBABuffer and fill in the pointers to output image
buf := (*C.WebPRGBABuffer)(unsafe.Pointer(&config.output.u[0]))
buf.rgba = (*C.uint8_t)(&img.Pix[0])
buf.stride = C.int(img.Stride)
buf.size = (C.size_t)(len(img.Pix))
// Decode
if status := C.WebPDecode(cDataPtr, cDataSize, config); status != C.VP8_STATUS_OK {
return nil, fmt.Errorf("Could not decode data stream, return %s", statusString(status))
}
return
}
// sattusString convert the VP8StatsCode to string.
func statusString(status C.VP8StatusCode) string {
switch status {
case C.VP8_STATUS_OK:
return "VP8_STATUS_OK"
case C.VP8_STATUS_OUT_OF_MEMORY:
return "VP8_STATUS_OUT_OF_MEMORY"
case C.VP8_STATUS_INVALID_PARAM:
return "VP8_STATUS_INVALID_PARAM"
case C.VP8_STATUS_BITSTREAM_ERROR:
return "VP8_STATUS_BITSTREAM_ERROR"
case C.VP8_STATUS_UNSUPPORTED_FEATURE:
return "VP8_STATUS_UNSUPPORTED_FEATURE"
case C.VP8_STATUS_SUSPENDED:
return "VP8_STATUS_SUSPENDED"
case C.VP8_STATUS_USER_ABORT:
return "VP8_STATUS_USER_ABORT"
case C.VP8_STATUS_NOT_ENOUGH_DATA:
return "VP8_STATUS_NOT_ENOUGH_DATA"
}
return "Unexpected Status Code"
}
// initDecoderConfing initializes a decoder configration and sets up the options.
func initDecoderConfig(options *DecoderOptions) (config *C.WebPDecoderConfig, err error) {
// Initialize decoder config
config = &C.WebPDecoderConfig{}
if C.WebPInitDecoderConfig(config) == 0 {
return nil, errors.New("Could not initialize decoder config")
}
// Set up decoder options
if options.BypassFiltering {
config.options.bypass_filtering = 1
}
if options.NoFancyUpsampling {
config.options.no_fancy_upsampling = 1
}
if options.Crop.Max.X > 0 && options.Crop.Max.Y > 0 {
config.options.use_cropping = 1
config.options.crop_left = C.int(options.Crop.Min.X)
config.options.crop_top = C.int(options.Crop.Min.Y)
config.options.crop_width = C.int(options.Crop.Dx())
config.options.crop_height = C.int(options.Crop.Dy())
}
if options.Scale.Max.X > 0 && options.Scale.Max.Y > 0 {
config.options.use_scaling = 1
config.options.scaled_width = C.int(options.Scale.Max.X)
config.options.scaled_height = C.int(options.Scale.Max.Y)
}
if options.UseThreads {
config.options.use_threads = 1
}
config.options.dithering_strength = C.int(options.DitheringStrength)
return
}
// calcOutputSize retrives width and height of output image from the decoder configuration.
func calcOutputSize(config *C.WebPDecoderConfig) (width, height int) {
options := config.options
if options.use_scaling > 0 {
width = int(config.options.scaled_width)
height = int(config.options.scaled_height)
return
}
if config.options.use_cropping > 0 {
width = int(config.options.crop_width)
height = int(config.options.crop_height)
return
}
width = int(config.input.width)
height = int(config.input.height)
return
}

139
webp/encode.go Normal file
View file

@ -0,0 +1,139 @@
package webp
/*
#cgo LDFLAGS: -lwebp
#include <stdlib.h>
#include <webp/encode.h>
int writeWebP(uint8_t*, size_t, struct WebPPicture*);
*/
import "C"
import (
"errors"
"fmt"
"image"
"io"
"unsafe"
)
// Config specifies WebP encoding configuration.
type Config struct {
Preset Preset // Parameters Preset
Lossless bool // True if use lossless encoding
Quality float32 // WebP quality factor, 0-100
Method int // Quality/Speed trade-off factor, 0=faster / 6=slower-better
TargetSize int // Target size of encoded file in bytes
TargetPSNR float32 // Target PSNR, takes precedence over TargetSize
Segments int // Maximum number of segments to use, 1..4
SNSStrength int // Strength of spartial noise shaping, 0..100=maximum
FilterStrength int // Strength of filter, 0..100=strength
FilterSharpness int // Sharpness of filter, 0..7=sharpness
FilterType FilterType // Filtering type
Pass int // Number of entropy-analysis passes, 0..100
}
type destinationManager struct {
writer io.Writer
}
//export writeWebP
func writeWebP(data *C.uint8_t, size C.size_t, pic *C.WebPPicture) C.int {
mgr := (*destinationManager)(unsafe.Pointer(pic.custom_ptr))
bytes := C.GoBytes(unsafe.Pointer(data), C.int(size))
_, err := mgr.writer.Write(bytes)
if err != nil {
return 0 // TODO: can't pass error message
}
return 1
}
// EncodeRGBA encodes and writes image.Image into the writer as WebP.
// Now supports image.RGBA or image.NRGBA.
func EncodeRGBA(w io.Writer, img image.Image, c Config) (err error) {
webpConfig, err := initConfig(c)
if err != nil {
return
}
var pic C.WebPPicture
if C.WebPPictureInit(&pic) == 0 {
return errors.New("Could not initialize webp picture")
}
pic.use_argb = 1
pic.width = C.int(img.Bounds().Dx())
pic.height = C.int(img.Bounds().Dy())
pic.writer = C.WebPWriterFunction(C.writeWebP)
pic.custom_ptr = unsafe.Pointer(&destinationManager{writer: w})
switch p := img.(type) {
case *image.RGBA:
C.WebPPictureImportRGBA(&pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride))
case *image.NRGBA:
C.WebPPictureImportRGBA(&pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride))
default:
return errors.New("unsupported image type")
}
defer C.WebPPictureFree(&pic)
if C.WebPEncode(webpConfig, &pic) == 0 {
return fmt.Errorf("Encoding error: %d", pic.error_code)
}
return
}
// EncodeYUVA encodes and writes YUVA Image data into the writer as WebP.
func EncodeYUVA(w io.Writer, img *YUVAImage, c Config) (err error) {
webpConfig, err := initConfig(c)
if err != nil {
return
}
var pic C.WebPPicture
if C.WebPPictureInit(&pic) == 0 {
return errors.New("Could not initialize webp picture")
}
pic.use_argb = 0
pic.colorspace = C.WebPEncCSP(img.ColorSpace)
pic.width = C.int(img.Rect.Dx())
pic.height = C.int(img.Rect.Dy())
pic.y = (*C.uint8_t)(&img.Y[0])
pic.u = (*C.uint8_t)(&img.Cb[0])
pic.v = (*C.uint8_t)(&img.Cr[0])
pic.y_stride = C.int(img.YStride)
pic.uv_stride = C.int(img.CStride)
if img.ColorSpace == YUV420A {
pic.a = (*C.uint8_t)(&img.A[0])
pic.a_stride = C.int(img.AStride)
}
pic.writer = C.WebPWriterFunction(C.writeWebP)
pic.custom_ptr = unsafe.Pointer(&destinationManager{writer: w})
if C.WebPEncode(webpConfig, &pic) == 0 {
return fmt.Errorf("Encoding error: %d", pic.error_code)
}
return
}
// initConfig initializes C.WebPConfig with encoding parameters.
func initConfig(c Config) (config *C.WebPConfig, err error) {
config = &C.WebPConfig{}
if C.WebPConfigPreset(config, C.WebPPreset(c.Preset), C.float(c.Quality)) == 0 {
return nil, errors.New("Could not initialize configuration with preset")
}
config.target_size = C.int(c.TargetSize)
config.target_PSNR = C.float(c.TargetPSNR)
if C.WebPValidateConfig(config) == 0 {
return nil, errors.New("Invalid configuration")
}
return
}

48
webp/webp.go Normal file
View file

@ -0,0 +1,48 @@
package webp
/*
#cgo LDFLAGS: -lwebp
#include <stdlib.h>
#include <webp/encode.h>
*/
import "C"
// ColorSpace represents encoding color space in WebP
type ColorSpace int
const (
// YUV420 specifies YUV4:2:0
YUV420 ColorSpace = C.WEBP_YUV420
// YUV420A specifies YUV4:2:0 with alpha channel
YUV420A ColorSpace = C.WEBP_YUV420A
)
// Preset corresponds to C.WebPPreset
type Preset int
const (
// PresetDefault corresponds to WEBP_PRESET_DEFAULT, for default preset.
PresetDefault Preset = C.WEBP_PRESET_DEFAULT
// PresetPicture corresponds to WEBP_PRESET_PICTURE, for digital picture, like portrait, inner shot
PresetPicture Preset = C.WEBP_PRESET_PICTURE
// PresetPhoto corresponds to WEBP_PRESET_PHOTO, for outdoor photograph, with natural lighting
PresetPhoto Preset = C.WEBP_PRESET_PHOTO
// PresetDrawing corresponds to WEBP_PRESET_DRAWING, for hand or line drawing, with high-contrast details
PresetDrawing Preset = C.WEBP_PRESET_DRAWING
// PresetIcon corresponds to WEBP_PRESET_ICON, for small-sized colorful images
PresetIcon Preset = C.WEBP_PRESET_ICON
// PresetText corresponds to WEBP_PRESET_TEXT, for text-like
PresetText Preset = C.WEBP_PRESET_TEXT
)
// FilterType corresponds to filter types in compression parameters.
type FilterType int
const (
// SimpleFilter (=0, default)
SimpleFilter FilterType = iota
// StrongFilter (=1)
StrongFilter
)

236
webp/webp_test.go Normal file
View file

@ -0,0 +1,236 @@
package webp_test
import (
"bufio"
"image"
"image/png"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/harukasan/go-libwebp/webp"
)
//
// Utitlities of input/output example images
//
// GetExFilePath returns the path of specified example file.
func GetExFilePath(name string) string {
return filepath.Join(os.Getenv("GOPATH"), "src/github.com/harukasan/go-libwebp/examples/images", name)
}
// OpenExFile opens specified example file
func OpenExFile(name string) (io io.Reader) {
io, err := os.Open(GetExFilePath(name))
if err != nil {
panic(err)
}
return
}
// ReadExFile reads and returns data bytes of specified example file.
func ReadExFile(name string) (data []byte) {
data, err := ioutil.ReadFile(GetExFilePath(name))
if err != nil {
panic(err)
}
return
}
// CreateExOutFile creates output file into example/out directory.
func CreateExOutFile(name string) (file *os.File) {
// Make output directory
dir := filepath.Join(os.Getenv("GOPATH"), "src/github.com/harukasan/go-libwebp/examples/out")
if err := os.Mkdir(dir, 0755); err != nil && os.IsNotExist(err) {
panic(err)
}
// Create example output file
file, err := os.Create(filepath.Join(dir, name))
if err != nil {
panic(err)
}
return
}
//
// Decode
//
// Test Get Decoder Version
func TestGetDecoderVersion(t *testing.T) {
v := webp.GetDecoderVersion()
if v < 0 {
t.Errorf("GetDecoderVersion should returns positive version number, got %v\n", v)
}
}
func TestGetInfo(t *testing.T) {
data := ReadExFile("cosmos.webp")
width, height := webp.GetInfo(data)
if width != 1024 {
t.Errorf("Expected width: %d, but got %d", 1024, width)
}
if height != 768 {
t.Errorf("Expected height: %d, but got %d", 768, height)
}
}
func TestGetFeatures(t *testing.T) {
data := ReadExFile("cosmos.webp")
f, err := webp.GetFeatures(data)
if err != nil {
t.Errorf("Got Error: %v", err)
return
}
if got := f.Width; got != 1024 {
t.Errorf("Expected Width: %v, but got %v", 1024, got)
}
if got := f.Height; got != 768 {
t.Errorf("Expected Width: %v, but got %v", 768, got)
}
if got := f.HasAlpha; got != false {
t.Errorf("Expected HasAlpha: %v, but got %v", false, got)
}
if got := f.HasAnimation; got != false {
t.Errorf("Expected HasAlpha: %v, but got %v", false, got)
}
if got := f.Format; got != 1 {
t.Errorf("Expected Format: %v, but got %v", 1, got)
}
}
func TestDecodeYUV(t *testing.T) {
files := []string{
"cosmos.webp",
"butterfly.webp",
"kinkaku.webp",
"yellow-rose-3.webp",
}
for _, file := range files {
data := ReadExFile(file)
options := &webp.DecoderOptions{}
_, err := webp.DecodeYUVA(data, options)
if err != nil {
t.Errorf("Got Error: %v", err)
return
}
}
}
func TestDecodeRGBA(t *testing.T) {
files := []string{
"cosmos.webp",
"butterfly.webp",
"kinkaku.webp",
"yellow-rose-3.webp",
}
for _, file := range files {
data := ReadExFile(file)
options := &webp.DecoderOptions{}
_, err := webp.DecodeRGBA(data, options)
if err != nil {
t.Errorf("Got Error: %v", err)
return
}
}
}
func TestDecodeRGBAWithCropping(t *testing.T) {
data := ReadExFile("cosmos.webp")
crop := image.Rect(100, 100, 300, 200)
options := &webp.DecoderOptions{
Crop: crop,
}
img, err := webp.DecodeRGBA(data, options)
if err != nil {
t.Errorf("Got Error: %v", err)
return
}
if img.Rect.Dx() != crop.Dx() || img.Rect.Dy() != crop.Dy() {
t.Errorf("Decoded image should cropped to %v, but got %v", crop, img.Rect)
}
}
func TestDecodeRGBAWithScaling(t *testing.T) {
data := ReadExFile("cosmos.webp")
scale := image.Rect(0, 0, 640, 480)
options := &webp.DecoderOptions{
Scale: scale,
}
img, err := webp.DecodeRGBA(data, options)
if err != nil {
t.Errorf("Got Error: %v", err)
return
}
if img.Rect.Dx() != scale.Dx() || img.Rect.Dy() != scale.Dy() {
t.Errorf("Decoded image should scaled to %v, but got %v", scale, img.Rect)
}
}
//
// Encoding
//
func TestEncodeRGBA(t *testing.T) {
img, _ := png.Decode(OpenExFile("yellow-rose-3.png"))
config := webp.Config{
Preset: webp.PresetDefault,
Quality: 100,
Method: 6,
}
f := CreateExOutFile("TestEncodeRGBA.webp")
w := bufio.NewWriter(f)
defer func() {
w.Flush()
f.Close()
}()
if err := webp.EncodeRGBA(w, img, config); err != nil {
t.Errorf("Got Error: %v", err)
return
}
}
func TestEncodeYUVA(t *testing.T) {
data := ReadExFile("cosmos.webp")
options := &webp.DecoderOptions{}
img, err := webp.DecodeYUVA(data, options)
if err != nil {
t.Errorf("Got Error: %v in decoding", err)
return
}
f := CreateExOutFile("TestEncodeYUVA.webp")
w := bufio.NewWriter(f)
defer func() {
w.Flush()
f.Close()
}()
config := webp.Config{
Preset: webp.PresetDefault,
Quality: 100,
Method: 6,
}
if err := webp.EncodeYUVA(w, img, config); err != nil {
t.Errorf("Got Error: %v", err)
return
}
}

56
webp/yuva_image.go Normal file
View file

@ -0,0 +1,56 @@
package webp
import "image"
// YUVAImage represents a image of YUV colors with alpha channel image.
//
// We can not insert WebP planer image into image.YCbCr,
// because image.YCbCr is not compatible with ITU-R BT.601, but JFIF/JPEG one.
//
// See: http://en.wikipedia.org/wiki/YCbCr
type YUVAImage struct {
Y, Cb, Cr, A []uint8
YStride int
CStride int
AStride int
ColorSpace ColorSpace
Rect image.Rectangle
}
// NewYUVAImage creates and allocates image buffer.
func NewYUVAImage(r image.Rectangle, c ColorSpace) (image *YUVAImage) {
yw, yh := r.Dx(), r.Dx()
cw, ch := ((r.Max.X+1)/2 - r.Min.X/2), ((r.Max.Y+1)/2 - r.Min.Y/2)
switch c {
case YUV420:
b := make([]byte, yw*yh+2*cw*ch)
image = &YUVAImage{
Y: b[:yw*yh],
Cb: b[yw*yh+0*cw*ch : yw*yh+1*cw*ch],
Cr: b[yw*yh+1*cw*ch : yw*yh+2*cw*ch],
A: nil,
YStride: yw,
CStride: cw,
AStride: 0,
ColorSpace: c,
Rect: r,
}
case YUV420A:
b := make([]byte, 2*yw*yh+2*cw*ch)
image = &YUVAImage{
Y: b[:yw*yh],
Cb: b[yw*yh+0*cw*ch : yw*yh+1*cw*ch],
Cr: b[yw*yh+1*cw*ch : yw*yh+2*cw*ch],
A: b[yw*yh+2*cw*ch:],
YStride: yw,
CStride: cw,
AStride: yw,
ColorSpace: c,
Rect: r,
}
}
return
}