package webp /* #cgo LDFLAGS: -lwebp #include #include 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 }