mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-15 10:25:55 -07:00
parent
58b2be80a5
commit
cc2290cb45
8 changed files with 305 additions and 88 deletions
44
README.md
44
README.md
|
|
@ -1,12 +1,12 @@
|
||||||
# bimg [](https://travis-ci.org/h2non/bimg) [](https://github.com/h2non/bimg/releases) [](https://godoc.org/github.com/h2non/bimg) [](https://coveralls.io/r/h2non/bimg?branch=master)
|
# bimg [](https://travis-ci.org/h2non/bimg) [](https://github.com/h2non/bimg/releases) [](https://godoc.org/github.com/h2non/bimg) [](https://coveralls.io/r/h2non/bimg?branch=master)
|
||||||
|
|
||||||
Small [Go](http://golang.org) library for blazing fast and efficient image processing based on [libvips](https://github.com/jcupitt/libvips) using C bindings. It provides a clean, simple and fluent [API](#examples) in pure Go.
|
Small [Go](http://golang.org) library for blazing fast and efficient image processing based on [libvips](https://github.com/jcupitt/libvips) using C bindings. It provides a clean, simple and fluent [API](https://godoc.org/github.com/h2non/bimg) in pure Go.
|
||||||
|
|
||||||
bimg is designed to be a small and efficient library with a generic and useful set of features.
|
bimg is designed to be a small and efficient library with a generic and useful set of features.
|
||||||
It uses internally libvips, which requires a [low memory footprint](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
|
It uses internally libvips, which requires a [low memory footprint](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
|
||||||
and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native `image` package, and in some cases it's even 8x faster processing JPEG images.
|
and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native `image` package, and in some cases it's even 8x faster processing JPEG images.
|
||||||
|
|
||||||
It can read JPEG, PNG, WEBP and TIFF formats and output to JPEG, PNG and WEBP. It supports common [image transformation](#supported-image-operations) operations such as crop, resize, rotate... and conversion between multiple formats.
|
It can read JPEG, PNG, WEBP and TIFF formats and output to JPEG, PNG and WEBP. It supports common [image transformation](#supported-image-operations) operations such as crop, resize, rotate, zoom, watermark... and conversion between multiple formats.
|
||||||
|
|
||||||
For getting started, take a look to the [examples](#examples) and [programmatic API](https://godoc.org/github.com/h2non/bimg) documentation.
|
For getting started, take a look to the [examples](#examples) and [programmatic API](https://godoc.org/github.com/h2non/bimg) documentation.
|
||||||
|
|
||||||
|
|
@ -41,13 +41,14 @@ The [install script](https://github.com/lovell/sharp/blob/master/preinstall.sh)
|
||||||
- Resize
|
- Resize
|
||||||
- Enlarge
|
- Enlarge
|
||||||
- Crop
|
- Crop
|
||||||
- Rotate (and auto-rotate based on EXIF orientation)
|
- Rotate (with auto-rotate based on EXIF orientation)
|
||||||
- Flip (and auto-flip based on EXIF metadata)
|
- Flip (with auto-flip based on EXIF metadata)
|
||||||
- Flop
|
- Flop
|
||||||
- Zoom
|
- Zoom
|
||||||
- Thumbnail
|
- Thumbnail
|
||||||
- Extract area
|
- Extract area
|
||||||
- Format conversion
|
- Watermark (fully customizable text-based)
|
||||||
|
- Format conversion (with additional quality/compression settings)
|
||||||
- EXIF metadata (size, alpha channel, profile, orientation...)
|
- EXIF metadata (size, alpha channel, profile, orientation...)
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
@ -165,6 +166,34 @@ if err != nil {
|
||||||
bimg.Write("new.jpg", newImage)
|
bimg.Write("new.jpg", newImage)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Watermark
|
||||||
|
|
||||||
|
```go
|
||||||
|
buffer, err := bimg.Read("image.jpg")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options := bimg.Watermark{
|
||||||
|
Watermark{
|
||||||
|
Text: "Chuck Norris - Copyright (c) 2315",
|
||||||
|
Opacity: 0.25,
|
||||||
|
Width: 200,
|
||||||
|
DPI: 100,
|
||||||
|
Margin: 150,
|
||||||
|
Font: "sans bold 12",
|
||||||
|
Background: bimg.Color{255, 255, 255},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newImage, err := bimg.NewImage(buffer).Watermark()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bimg.Write("new.jpg", newImage)
|
||||||
|
```
|
||||||
|
|
||||||
#### Fluent interface
|
#### Fluent interface
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
@ -491,7 +520,6 @@ Determines the image type format (jpeg, png, webp or tiff)
|
||||||
type Interpolator int
|
type Interpolator int
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
const (
|
const (
|
||||||
BICUBIC Interpolator = iota
|
BICUBIC Interpolator = iota
|
||||||
|
|
@ -531,6 +559,10 @@ type Options struct {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
- [John Cupitt](https://github.com/jcupitt)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT - Tomas Aparicio
|
MIT - Tomas Aparicio
|
||||||
|
|
|
||||||
15
image.go
15
image.go
|
|
@ -75,20 +75,9 @@ func (i *Image) Thumbnail(pixels int) ([]byte, error) {
|
||||||
return i.Process(options)
|
return i.Process(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert an image. Alias to Watermark()
|
|
||||||
func (i *Image) Insert(image []byte, left, top int) ([]byte, error) {
|
|
||||||
return i.Watermark(image, left, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert an image to the existent one as watermark
|
// Insert an image to the existent one as watermark
|
||||||
func (i *Image) Watermark(image []byte, left, top int) ([]byte, error) {
|
func (i *Image) Watermark(w Watermark) ([]byte, error) {
|
||||||
options := Options{
|
options := Options{Watermark: w}
|
||||||
Insert: Insert{
|
|
||||||
Buffer: image,
|
|
||||||
Top: top,
|
|
||||||
Left: left,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return i.Process(options)
|
return i.Process(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,8 +111,13 @@ func TestImageWatermark(t *testing.T) {
|
||||||
t.Errorf("Cannot process the image: %#v", err)
|
t.Errorf("Cannot process the image: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
insert, _ := Read("fixtures/watermark.png")
|
buf, err := image.Watermark(Watermark{
|
||||||
buf, err := image.Watermark(insert, 10, 10)
|
Text: "Copy me if you can",
|
||||||
|
Opacity: 0.5,
|
||||||
|
Width: 200,
|
||||||
|
DPI: 100,
|
||||||
|
Background: Color{255, 255, 255},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
options.go
21
options.go
|
|
@ -55,10 +55,20 @@ const (
|
||||||
VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL
|
VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL
|
||||||
)
|
)
|
||||||
|
|
||||||
type Insert struct {
|
// Color represents a traditional RGB color scheme
|
||||||
Top int
|
type Color struct {
|
||||||
Left int
|
R, G, B uint8
|
||||||
Buffer []byte
|
}
|
||||||
|
|
||||||
|
type Watermark struct {
|
||||||
|
Width int
|
||||||
|
DPI int
|
||||||
|
Margin int
|
||||||
|
Opacity float32
|
||||||
|
NoReplicate bool
|
||||||
|
Text string
|
||||||
|
Font string
|
||||||
|
Background Color
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
|
@ -78,9 +88,10 @@ type Options struct {
|
||||||
Flip bool
|
Flip bool
|
||||||
Flop bool
|
Flop bool
|
||||||
NoAutoRotate bool
|
NoAutoRotate bool
|
||||||
|
Colorspace bool
|
||||||
Rotate Angle
|
Rotate Angle
|
||||||
Insert Insert
|
|
||||||
Gravity Gravity
|
Gravity Gravity
|
||||||
|
Watermark Watermark
|
||||||
Type ImageType
|
Type ImageType
|
||||||
Interpolator Interpolator
|
Interpolator Interpolator
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
resize.go
50
resize.go
|
|
@ -124,8 +124,8 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
||||||
Compression: o.Compression,
|
Compression: o.Compression,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert an image if necessary
|
// watermark
|
||||||
image, err = insertImage(image, imageType, o.Insert, saveOptions)
|
image, err = watermakImage(image, o.Watermark)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -198,42 +198,44 @@ func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, e
|
||||||
return image, err
|
return image, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WIP
|
func watermakImage(image *C.struct__VipsImage, w Watermark) (*C.struct__VipsImage, error) {
|
||||||
func insertImage(image *C.struct__VipsImage, t ImageType, o Insert, save vipsSaveOptions) (*C.struct__VipsImage, error) {
|
if len(w.Text) == 0 {
|
||||||
if len(o.Buffer) == 0 {
|
|
||||||
return image, nil
|
return image, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
insert, imageType, err := vipsRead(o.Buffer)
|
// Defaults
|
||||||
|
if len(w.Font) == 0 {
|
||||||
|
w.Font = "sans 10"
|
||||||
|
}
|
||||||
|
if w.Width == 0 {
|
||||||
|
w.Width = int(math.Floor(float64(image.Xsize / 8)))
|
||||||
|
}
|
||||||
|
if w.DPI == 0 {
|
||||||
|
w.DPI = 150
|
||||||
|
}
|
||||||
|
if w.Margin == 0 {
|
||||||
|
w.Margin = w.Width
|
||||||
|
}
|
||||||
|
if w.Opacity == 0 {
|
||||||
|
w.Opacity = 0.25
|
||||||
|
} else if w.Opacity > 1 {
|
||||||
|
w.Opacity = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := vipsWatermark(image, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if imageType != t {
|
return image, nil
|
||||||
save.Type = t
|
|
||||||
buf, err := vipsSave(insert, save)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
insert, imageType, err = vipsRead(buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("Insert image: %#v", insert)
|
|
||||||
|
|
||||||
return vipsInsert(image, insert, o.Left, o.Top)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func zoomImage(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, error) {
|
func zoomImage(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, error) {
|
||||||
if zoom == 0 {
|
if zoom == 0 {
|
||||||
return image, nil
|
return image, nil
|
||||||
}
|
}
|
||||||
zoom += 1
|
|
||||||
|
|
||||||
return vipsZoom(image, zoom)
|
return vipsZoom(image, zoom+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
|
func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,44 @@ func TestRotate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testColorspace(t *testing.T) {
|
||||||
|
options := Options{Colorspace: true}
|
||||||
|
buf, _ := Read("fixtures/sky.jpg")
|
||||||
|
|
||||||
|
newImg, err := Resize(buf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if DetermineImageType(newImg) != JPEG {
|
||||||
|
t.Fatal("Image is not jpeg")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Write("fixtures/test_color_out.jpg", newImg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot save the image")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCorruptedImage(t *testing.T) {
|
||||||
|
options := Options{Width: 800, Height: 600}
|
||||||
|
buf, _ := Read("fixtures/corrupt.jpg")
|
||||||
|
|
||||||
|
newImg, err := Resize(buf, options)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if DetermineImageType(newImg) != JPEG {
|
||||||
|
t.Fatal("Image is not jpeg")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Write("fixtures/test_corrupt_out.jpg", newImg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot save the image")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInvalidRotate(t *testing.T) {
|
func TestInvalidRotate(t *testing.T) {
|
||||||
options := Options{Width: 800, Height: 600, Rotate: 111}
|
options := Options{Width: 800, Height: 600, Rotate: 111}
|
||||||
buf, _ := Read("fixtures/test.jpg")
|
buf, _ := Read("fixtures/test.jpg")
|
||||||
|
|
|
||||||
115
vips.go
115
vips.go
|
|
@ -3,6 +3,7 @@ package bimg
|
||||||
/*
|
/*
|
||||||
#cgo pkg-config: vips
|
#cgo pkg-config: vips
|
||||||
#include "vips.h"
|
#include "vips.h"
|
||||||
|
#include "stdlib.h"
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
|
@ -19,16 +20,30 @@ var (
|
||||||
initialized bool = false
|
initialized bool = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type VipsMemoryInfo struct {
|
||||||
|
Memory int64
|
||||||
|
MemoryHighwater int64
|
||||||
|
Allocations int64
|
||||||
|
}
|
||||||
|
|
||||||
type vipsSaveOptions struct {
|
type vipsSaveOptions struct {
|
||||||
Quality int
|
Quality int
|
||||||
Compression int
|
Compression int
|
||||||
Type ImageType
|
Type ImageType
|
||||||
}
|
}
|
||||||
|
|
||||||
type VipsMemoryInfo struct {
|
type vipsWatermarkOptions struct {
|
||||||
Memory int64
|
Width C.int
|
||||||
MemoryHighwater int64
|
DPI C.int
|
||||||
Allocations int64
|
Margin C.int
|
||||||
|
NoReplicate C.int
|
||||||
|
Opacity C.float
|
||||||
|
Background [3]C.double
|
||||||
|
}
|
||||||
|
|
||||||
|
type vipsWatermarkTextOptions struct {
|
||||||
|
Text *C.char
|
||||||
|
Font *C.char
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -71,12 +86,12 @@ func Shutdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output to stdout collected data for debugging purposes
|
// Output to stdout vips collected data. Useful for debugging
|
||||||
func VipsDebug() {
|
func VipsDebug() {
|
||||||
C.im__print_all()
|
C.im__print_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get memory info stats from vips
|
// Get memory info stats from vips (cache size, memory allocs...)
|
||||||
func VipsMemory() VipsMemoryInfo {
|
func VipsMemory() VipsMemoryInfo {
|
||||||
return VipsMemoryInfo{
|
return VipsMemoryInfo{
|
||||||
Memory: int64(C.vips_tracked_get_mem()),
|
Memory: int64(C.vips_tracked_get_mem()),
|
||||||
|
|
@ -85,6 +100,26 @@ func VipsMemory() VipsMemoryInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vipsExifOrientation(image *C.struct__VipsImage) int {
|
||||||
|
return int(C.vips_exif_orientation(image))
|
||||||
|
}
|
||||||
|
|
||||||
|
func vipsHasAlpha(image *C.struct__VipsImage) bool {
|
||||||
|
return int(C.has_alpha_channel(image)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func vipsHasProfile(image *C.struct__VipsImage) bool {
|
||||||
|
return int(C.has_profile_embed(image)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func vipsWindowSize(name string) float64 {
|
||||||
|
return float64(C.interpolator_window_size(C.CString(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func vipsSpace(image *C.struct__VipsImage) string {
|
||||||
|
return C.GoString(C.vips_enum_nick_bridge(image))
|
||||||
|
}
|
||||||
|
|
||||||
func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, error) {
|
func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, error) {
|
||||||
var out *C.struct__VipsImage
|
var out *C.struct__VipsImage
|
||||||
defer C.g_object_unref(C.gpointer(image))
|
defer C.g_object_unref(C.gpointer(image))
|
||||||
|
|
@ -121,13 +156,54 @@ func vipsZoom(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, error
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipsInsert(image *C.struct__VipsImage, sub *C.struct__VipsImage, left, top int) (*C.struct__VipsImage, error) {
|
func vipsColorSpace(image *C.struct__VipsImage) (*C.struct__VipsImage, error) {
|
||||||
var out *C.struct__VipsImage
|
var out *C.struct__VipsImage
|
||||||
|
var temp *C.struct__VipsImage
|
||||||
|
var max *C.double
|
||||||
|
var x *C.int
|
||||||
|
var y *C.int
|
||||||
|
|
||||||
defer C.g_object_unref(C.gpointer(image))
|
defer C.g_object_unref(C.gpointer(image))
|
||||||
defer C.g_object_unref(C.gpointer(sub))
|
|
||||||
|
|
||||||
err := C.vips_insert_bridge(image, sub, &out, C.int(left), C.int(top))
|
err := C.vips_colorspace_bridge(image, &out)
|
||||||
|
if err != 0 {
|
||||||
|
return nil, catchVipsError()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = C.vips_hist_find_ndim_bridge(out, &temp)
|
||||||
|
if err != 0 {
|
||||||
|
return nil, catchVipsError()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = C.vips_max_bridge(temp, max, &x, &y)
|
||||||
|
if err != 0 {
|
||||||
|
return nil, catchVipsError()
|
||||||
|
}
|
||||||
|
debug("MAX VALUE %dx%d", x, y)
|
||||||
|
|
||||||
|
return temp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func vipsWatermark(image *C.struct__VipsImage, w Watermark) (*C.struct__VipsImage, error) {
|
||||||
|
var out *C.struct__VipsImage
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
noReplicate := 0
|
||||||
|
if w.NoReplicate {
|
||||||
|
noReplicate = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
text := C.CString(w.Text)
|
||||||
|
font := C.CString(w.Font)
|
||||||
|
background := [3]C.double{C.double(w.Background.R), C.double(w.Background.G), C.double(w.Background.B)}
|
||||||
|
|
||||||
|
textOpts := vipsWatermarkTextOptions{text, font}
|
||||||
|
opts := vipsWatermarkOptions{C.int(w.Width), C.int(w.DPI), C.int(w.Margin), C.int(noReplicate), C.float(w.Opacity), background}
|
||||||
|
|
||||||
|
defer C.free(unsafe.Pointer(text))
|
||||||
|
defer C.free(unsafe.Pointer(font))
|
||||||
|
|
||||||
|
err := C.vips_watermark(image, &out, (*C.watermarkTextOptions)(unsafe.Pointer(&textOpts)), (*C.watermarkOptions)(unsafe.Pointer(&opts)))
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, catchVipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
|
|
@ -247,7 +323,6 @@ func vipsAffine(input *C.struct__VipsImage, residual float64, i Interpolator) (*
|
||||||
defer C.g_object_unref(C.gpointer(input))
|
defer C.g_object_unref(C.gpointer(input))
|
||||||
defer C.g_object_unref(C.gpointer(interpolator))
|
defer C.g_object_unref(C.gpointer(interpolator))
|
||||||
|
|
||||||
// Perform affine transformation
|
|
||||||
err := C.vips_affine_interpolator(input, &image, C.double(residual), 0, 0, C.double(residual), interpolator)
|
err := C.vips_affine_interpolator(input, &image, C.double(residual), 0, 0, C.double(residual), interpolator)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, catchVipsError()
|
return nil, catchVipsError()
|
||||||
|
|
@ -288,26 +363,6 @@ func vipsImageType(buf []byte) ImageType {
|
||||||
return imageType
|
return imageType
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipsExifOrientation(image *C.struct__VipsImage) int {
|
|
||||||
return int(C.vips_exif_orientation(image))
|
|
||||||
}
|
|
||||||
|
|
||||||
func vipsHasAlpha(image *C.struct__VipsImage) bool {
|
|
||||||
return int(C.has_alpha_channel(image)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func vipsHasProfile(image *C.struct__VipsImage) bool {
|
|
||||||
return int(C.has_profile_embed(image)) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func vipsWindowSize(name string) float64 {
|
|
||||||
return float64(C.interpolator_window_size(C.CString(name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func vipsSpace(image *C.struct__VipsImage) string {
|
|
||||||
return C.GoString(C.vips_enum_nick_bridge(image))
|
|
||||||
}
|
|
||||||
|
|
||||||
func catchVipsError() error {
|
func catchVipsError() error {
|
||||||
s := C.GoString(C.vips_error_buffer())
|
s := C.GoString(C.vips_error_buffer())
|
||||||
C.vips_error_clear()
|
C.vips_error_clear()
|
||||||
|
|
|
||||||
101
vips.h
101
vips.h
|
|
@ -11,6 +11,20 @@ enum types {
|
||||||
MAGICK
|
MAGICK
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *Text;
|
||||||
|
char *Font;
|
||||||
|
} watermarkTextOptions;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int Width;
|
||||||
|
int DPI;
|
||||||
|
int Margin;
|
||||||
|
int NoReplicate;
|
||||||
|
float Opacity;
|
||||||
|
double Background[3];
|
||||||
|
} watermarkOptions;
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator)
|
vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator)
|
||||||
{
|
{
|
||||||
|
|
@ -98,9 +112,22 @@ vips_zoom_bridge(VipsImage *in, VipsImage **out, int xfac, int yfac)
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_insert_bridge(VipsImage *in, VipsImage *sub, VipsImage **out, int left, int top)
|
vips_colorspace_bridge(VipsImage *in, VipsImage **out)
|
||||||
{
|
{
|
||||||
return vips_insert(in, sub, out, left, top, NULL);
|
return vips_colourspace(in, out, VIPS_INTERPRETATION_LAB, NULL);
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_hist_find_ndim_bridge(VipsImage *in, VipsImage **out)
|
||||||
|
{
|
||||||
|
return vips_hist_find_ndim(in, out, "bins", 5, NULL);
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_max_bridge(VipsImage *in, double *out, int **x, int **y)
|
||||||
|
{
|
||||||
|
double ones[3] = { 1, 1, 1 };
|
||||||
|
return vips_max(in, ones, "x", x, "y", y, NULL);
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -109,12 +136,6 @@ vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width,
|
||||||
return vips_embed(in, out, left, top, width, height, "extend", extend, NULL);
|
return vips_embed(in, out, left, top, width, height, "extend", extend, NULL);
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
|
||||||
vips_colourspace_bridge(VipsImage *in, VipsImage **out, VipsInterpretation space)
|
|
||||||
{
|
|
||||||
return vips_colourspace(in, out, space, NULL);
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_extract_area_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height)
|
vips_extract_area_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height)
|
||||||
{
|
{
|
||||||
|
|
@ -165,3 +186,67 @@ vips_init_image(void *buf, size_t len, int imageType, VipsImage **out) {
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_watermark(VipsImage *in, VipsImage **out, watermarkTextOptions *to, watermarkOptions *o)
|
||||||
|
{
|
||||||
|
double ones[3] = { 1, 1, 1 };
|
||||||
|
|
||||||
|
VipsImage *base = vips_image_new();
|
||||||
|
VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 12);
|
||||||
|
t[0] = in;
|
||||||
|
|
||||||
|
// Make the mask.
|
||||||
|
if (
|
||||||
|
vips_text(&t[1], to->Text,
|
||||||
|
"width", o->Width,
|
||||||
|
"dpi", o->DPI,
|
||||||
|
"font", to->Font,
|
||||||
|
NULL) ||
|
||||||
|
vips_linear1(t[1], &t[2], o->Opacity, 0.0, NULL) ||
|
||||||
|
vips_cast(t[2], &t[3], VIPS_FORMAT_UCHAR, NULL) ||
|
||||||
|
vips_embed(t[3], &t[4], 100, 100,
|
||||||
|
t[3]->Xsize + o->Margin, t[3]->Ysize + o->Margin, NULL)
|
||||||
|
) {
|
||||||
|
g_object_unref(base);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate if necessary
|
||||||
|
if (o->NoReplicate != 1 && (
|
||||||
|
vips_replicate(t[4], &t[5],
|
||||||
|
1 + t[0]->Xsize / t[4]->Xsize,
|
||||||
|
1 + t[0]->Ysize / t[4]->Ysize, NULL) ||
|
||||||
|
vips_crop(t[5], &t[6], 0, 0,
|
||||||
|
t[0]->Xsize, t[0]->Ysize, NULL)
|
||||||
|
)) {
|
||||||
|
g_object_unref(base);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the constant image to paint the text with.
|
||||||
|
if (
|
||||||
|
vips_black(&t[7], 1, 1, NULL) ||
|
||||||
|
vips_linear( t[7], &t[8], ones, o->Background, 3, NULL) ||
|
||||||
|
vips_cast(t[8], &t[9], VIPS_FORMAT_UCHAR, NULL) ||
|
||||||
|
vips_copy(t[9], &t[10],
|
||||||
|
"interpretation", t[0]->Type,
|
||||||
|
NULL) ||
|
||||||
|
vips_embed(t[10], &t[11], 0, 0,
|
||||||
|
t[0]->Xsize, t[0]->Ysize,
|
||||||
|
"extend", VIPS_EXTEND_COPY,
|
||||||
|
NULL)
|
||||||
|
) {
|
||||||
|
g_object_unref(base);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend the mask and text and write to output.
|
||||||
|
if (vips_ifthenelse(t[6], t[11], t[0], out, "blend", TRUE, NULL)) {
|
||||||
|
g_object_unref(base);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(base);
|
||||||
|
return (0);
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue