mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-07 21:48:13 -08: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)
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -41,13 +41,14 @@ The [install script](https://github.com/lovell/sharp/blob/master/preinstall.sh)
|
|||
- Resize
|
||||
- Enlarge
|
||||
- Crop
|
||||
- Rotate (and auto-rotate based on EXIF orientation)
|
||||
- Flip (and auto-flip based on EXIF metadata)
|
||||
- Rotate (with auto-rotate based on EXIF orientation)
|
||||
- Flip (with auto-flip based on EXIF metadata)
|
||||
- Flop
|
||||
- Zoom
|
||||
- Thumbnail
|
||||
- Extract area
|
||||
- Format conversion
|
||||
- Watermark (fully customizable text-based)
|
||||
- Format conversion (with additional quality/compression settings)
|
||||
- EXIF metadata (size, alpha channel, profile, orientation...)
|
||||
|
||||
## Performance
|
||||
|
|
@ -165,6 +166,34 @@ if err != nil {
|
|||
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
|
||||
|
||||
```go
|
||||
|
|
@ -491,7 +520,6 @@ Determines the image type format (jpeg, png, webp or tiff)
|
|||
type Interpolator int
|
||||
```
|
||||
|
||||
|
||||
```go
|
||||
const (
|
||||
BICUBIC Interpolator = iota
|
||||
|
|
@ -531,6 +559,10 @@ type Options struct {
|
|||
}
|
||||
```
|
||||
|
||||
## Special Thanks
|
||||
|
||||
- [John Cupitt](https://github.com/jcupitt)
|
||||
|
||||
## License
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (i *Image) Watermark(image []byte, left, top int) ([]byte, error) {
|
||||
options := Options{
|
||||
Insert: Insert{
|
||||
Buffer: image,
|
||||
Top: top,
|
||||
Left: left,
|
||||
},
|
||||
}
|
||||
func (i *Image) Watermark(w Watermark) ([]byte, error) {
|
||||
options := Options{Watermark: w}
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,8 +111,13 @@ func TestImageWatermark(t *testing.T) {
|
|||
t.Errorf("Cannot process the image: %#v", err)
|
||||
}
|
||||
|
||||
insert, _ := Read("fixtures/watermark.png")
|
||||
buf, err := image.Watermark(insert, 10, 10)
|
||||
buf, err := image.Watermark(Watermark{
|
||||
Text: "Copy me if you can",
|
||||
Opacity: 0.5,
|
||||
Width: 200,
|
||||
DPI: 100,
|
||||
Background: Color{255, 255, 255},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
|
|
|||
21
options.go
21
options.go
|
|
@ -55,10 +55,20 @@ const (
|
|||
VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL
|
||||
)
|
||||
|
||||
type Insert struct {
|
||||
Top int
|
||||
Left int
|
||||
Buffer []byte
|
||||
// Color represents a traditional RGB color scheme
|
||||
type Color struct {
|
||||
R, G, B uint8
|
||||
}
|
||||
|
||||
type Watermark struct {
|
||||
Width int
|
||||
DPI int
|
||||
Margin int
|
||||
Opacity float32
|
||||
NoReplicate bool
|
||||
Text string
|
||||
Font string
|
||||
Background Color
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
|
|
@ -78,9 +88,10 @@ type Options struct {
|
|||
Flip bool
|
||||
Flop bool
|
||||
NoAutoRotate bool
|
||||
Colorspace bool
|
||||
Rotate Angle
|
||||
Insert Insert
|
||||
Gravity Gravity
|
||||
Watermark Watermark
|
||||
Type ImageType
|
||||
Interpolator Interpolator
|
||||
}
|
||||
|
|
|
|||
50
resize.go
50
resize.go
|
|
@ -124,8 +124,8 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
|||
Compression: o.Compression,
|
||||
}
|
||||
|
||||
// Insert an image if necessary
|
||||
image, err = insertImage(image, imageType, o.Insert, saveOptions)
|
||||
// watermark
|
||||
image, err = watermakImage(image, o.Watermark)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -198,42 +198,44 @@ func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, e
|
|||
return image, err
|
||||
}
|
||||
|
||||
// WIP
|
||||
func insertImage(image *C.struct__VipsImage, t ImageType, o Insert, save vipsSaveOptions) (*C.struct__VipsImage, error) {
|
||||
if len(o.Buffer) == 0 {
|
||||
func watermakImage(image *C.struct__VipsImage, w Watermark) (*C.struct__VipsImage, error) {
|
||||
if len(w.Text) == 0 {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if imageType != t {
|
||||
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)
|
||||
return image, nil
|
||||
}
|
||||
|
||||
func zoomImage(image *C.struct__VipsImage, zoom int) (*C.struct__VipsImage, error) {
|
||||
if zoom == 0 {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
options := Options{Width: 800, Height: 600, Rotate: 111}
|
||||
buf, _ := Read("fixtures/test.jpg")
|
||||
|
|
|
|||
115
vips.go
115
vips.go
|
|
@ -3,6 +3,7 @@ package bimg
|
|||
/*
|
||||
#cgo pkg-config: vips
|
||||
#include "vips.h"
|
||||
#include "stdlib.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
|
@ -19,16 +20,30 @@ var (
|
|||
initialized bool = false
|
||||
)
|
||||
|
||||
type VipsMemoryInfo struct {
|
||||
Memory int64
|
||||
MemoryHighwater int64
|
||||
Allocations int64
|
||||
}
|
||||
|
||||
type vipsSaveOptions struct {
|
||||
Quality int
|
||||
Compression int
|
||||
Type ImageType
|
||||
}
|
||||
|
||||
type VipsMemoryInfo struct {
|
||||
Memory int64
|
||||
MemoryHighwater int64
|
||||
Allocations int64
|
||||
type vipsWatermarkOptions struct {
|
||||
Width C.int
|
||||
DPI C.int
|
||||
Margin C.int
|
||||
NoReplicate C.int
|
||||
Opacity C.float
|
||||
Background [3]C.double
|
||||
}
|
||||
|
||||
type vipsWatermarkTextOptions struct {
|
||||
Text *C.char
|
||||
Font *C.char
|
||||
}
|
||||
|
||||
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() {
|
||||
C.im__print_all()
|
||||
}
|
||||
|
||||
// Get memory info stats from vips
|
||||
// Get memory info stats from vips (cache size, memory allocs...)
|
||||
func VipsMemory() VipsMemoryInfo {
|
||||
return VipsMemoryInfo{
|
||||
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) {
|
||||
var out *C.struct__VipsImage
|
||||
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
|
||||
}
|
||||
|
||||
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 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(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 {
|
||||
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(interpolator))
|
||||
|
||||
// Perform affine transformation
|
||||
err := C.vips_affine_interpolator(input, &image, C.double(residual), 0, 0, C.double(residual), interpolator)
|
||||
if err != 0 {
|
||||
return nil, catchVipsError()
|
||||
|
|
@ -288,26 +363,6 @@ func vipsImageType(buf []byte) 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 {
|
||||
s := C.GoString(C.vips_error_buffer())
|
||||
C.vips_error_clear()
|
||||
|
|
|
|||
101
vips.h
101
vips.h
|
|
@ -11,6 +11,20 @@ enum types {
|
|||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
int
|
||||
vips_colourspace_bridge(VipsImage *in, VipsImage **out, VipsInterpretation space)
|
||||
{
|
||||
return vips_colourspace(in, out, space, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
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;
|
||||
};
|
||||
|
||||
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