Skip to content

Commit

Permalink
Implement Image.Composite and blend modes
Browse files Browse the repository at this point in the history
  • Loading branch information
xzyfer committed Sep 1, 2018
1 parent 4eb8362 commit 165977e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 35 deletions.
7 changes: 7 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ func (i *Image) Trim() ([]byte, error) {
return i.Process(options)
}

// Composite blends two images together working from the bottom upwards, with
// the blend mode at each step being set by the corresponding BlendMode
func (i *Image) Composite(l *Image, mode BlendMode) ([]byte, error) {
options := Options{Composite: true, BlendMode: mode, CompositeLayers: []*Image{l}}
return i.Process(options)
}

// Process processes the image based on the given transformation options,
// talking with libvips bindings accordingly and returning the resultant
// image buffer.
Expand Down
9 changes: 9 additions & 0 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,15 @@ func TestImageTrimParameters(t *testing.T) {
Write("testdata/parameter_trim.png", buf)
}

func TestImageComposite(t *testing.T) {
overlay := initImage("transparent.png")
buf, err := initImage("test.jpg").Composite(overlay, BlendModeAdd)
if err != nil {
t.Errorf("Cannot process the image: %#v", err)
}
Write("testdata/test_composite_out.jpg", buf)
}

func TestImageLength(t *testing.T) {
i := initImage("test.jpg")

Expand Down
130 changes: 95 additions & 35 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,39 +188,99 @@ type Sharpen struct {

// Options represents the supported image transformation options.
type Options struct {
Height int
Width int
AreaHeight int
AreaWidth int
Top int
Left int
Quality int
Compression int
Zoom int
Crop bool
SmartCrop bool // Deprecated, use: bimg.Options.Gravity = bimg.GravitySmart
Enlarge bool
Embed bool
Flip bool
Flop bool
Force bool
NoAutoRotate bool
NoProfile bool
Interlace bool
StripMetadata bool
Trim bool
Lossless bool
Extend Extend
Rotate Angle
Background Color
Gravity Gravity
Watermark Watermark
WatermarkImage WatermarkImage
Type ImageType
Interpolator Interpolator
Interpretation Interpretation
GaussianBlur GaussianBlur
Sharpen Sharpen
Threshold float64
OutputICC string
Height int
Width int
AreaHeight int
AreaWidth int
Top int
Left int
Quality int
Compression int
Zoom int
Crop bool
SmartCrop bool // Deprecated, use: bimg.Options.Gravity = bimg.GravitySmart
Enlarge bool
Embed bool
Flip bool
Flop bool
Force bool
NoAutoRotate bool
NoProfile bool
Interlace bool
StripMetadata bool
Trim bool
Lossless bool
Composite bool
Extend Extend
Rotate Angle
Background Color
Gravity Gravity
Watermark Watermark
WatermarkImage WatermarkImage
Type ImageType
Interpolator Interpolator
Interpretation Interpretation
GaussianBlur GaussianBlur
Sharpen Sharpen
BlendMode BlendMode
Threshold float64
OutputICC string
CompositeLayers []*Image
}

// BlendMode represents the blend mode used when compositing.
// See: https://jcupitt.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
type BlendMode int

const (
// BlendModeClear where the second object is drawn, the first is removed
BlendModeClear BlendMode = C.VIPS_BLEND_MODE_CLEAR
// BlendModeSource the second object is drawn as if nothing were below
BlendModeSource BlendMode = C.VIPS_BLEND_MODE_SOURCE
// BlendModeOver the image shows what you would expect if you held two semi-transparent slides on top of each other
BlendModeOver BlendMode = C.VIPS_BLEND_MODE_OVER
// BlendModeIn the first object is removed completely, the second is only drawn where the first was
BlendModeIn BlendMode = C.VIPS_BLEND_MODE_IN
// BlendModeOut the second is drawn only where the first isn't
BlendModeOut BlendMode = C.VIPS_BLEND_MODE_OUT
// BlendModeAtop this leaves the first object mostly intact, but mixes both objects in the overlapping area
BlendModeAtop BlendMode = C.VIPS_BLEND_MODE_ATOP
// BlendModeDest leaves the first object untouched, the second is discarded completely
BlendModeDest BlendMode = C.VIPS_BLEND_MODE_DEST
// BlendModeDestOver like OVER, but swaps the arguments
BlendModeDestOver BlendMode = C.VIPS_BLEND_MODE_DEST_OVER
// BlendModeDestIn like IN, but swaps the arguments
BlendModeDestIn BlendMode = C.VIPS_BLEND_MODE_DEST_IN
// BlendModeDestOut like OUT, but swaps the arguments
BlendModeDestOut BlendMode = C.VIPS_BLEND_MODE_DEST_OUT
// BlendModeDestAtop like ATOP, but swaps the arguments
BlendModeDestAtop BlendMode = C.VIPS_BLEND_MODE_DEST_ATOP
// BlendModeXOR something like a difference operator
BlendModeXOR BlendMode = C.VIPS_BLEND_MODE_XOR
// BlendModeAdd a bit like adding the two images
BlendModeAdd BlendMode = C.VIPS_BLEND_MODE_ADD
// BlendModeSaturate a bit like the darker of the two
BlendModeSaturate BlendMode = C.VIPS_BLEND_MODE_SATURATE
// BlendModeMultiply at least as dark as the darker of the two inputs
BlendModeMultiply BlendMode = C.VIPS_BLEND_MODE_MULTIPLY
// BlendModeScreen at least as light as the lighter of the inputs
BlendModeScreen BlendMode = C.VIPS_BLEND_MODE_SCREEN
// BlendModeOverlay multiplies or screens colors, depending on the lightness
BlendModeOverlay BlendMode = C.VIPS_BLEND_MODE_OVERLAY
// BlendModeDarken the darker of each component
BlendModeDarken BlendMode = C.VIPS_BLEND_MODE_DARKEN
// BlendModeLighten the lighter of each component
BlendModeLighten BlendMode = C.VIPS_BLEND_MODE_LIGHTEN
// BlendModeColorDodge brighten first by a factor second
BlendModeColorDodge BlendMode = C.VIPS_BLEND_MODE_COLOUR_DODGE
// BlendModeColorBurn darken first by a factor of second
BlendModeColorBurn BlendMode = C.VIPS_BLEND_MODE_COLOUR_BURN
// BlendModeHardLight multiply or screen, depending on lightness
BlendModeHardLight BlendMode = C.VIPS_BLEND_MODE_HARD_LIGHT
// BlendModeSoftLight darken or lighten, depending on lightness
BlendModeSoftLight BlendMode = C.VIPS_BLEND_MODE_SOFT_LIGHT
// BlendModeDifference difference of the two
BlendModeDifference BlendMode = C.VIPS_BLEND_MODE_DIFFERENCE
// BlendModeExclusion somewhat like DIFFERENCE, but lower-contrast
BlendModeExclusion BlendMode = C.VIPS_BLEND_MODE_EXCLUSION
)
22 changes: 22 additions & 0 deletions resizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ func resizer(buf []byte, o Options) ([]byte, error) {
return nil, err
}

image, err = compositeImage(image, o.CompositeLayers, o.BlendMode)
if err != nil {
return nil, err
}

// Transform image, if necessary
if shouldTransformImage(o, inWidth, inHeight) {
image, err = transformImage(image, o, shrink, residual)
Expand Down Expand Up @@ -385,6 +390,23 @@ func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
return vipsZoom(image, zoom+1)
}

func compositeImage(image *C.VipsImage, layers []*Image, mode BlendMode) (*C.VipsImage, error) {
if mode == 0 {
return image, nil
}

inputs := make([]*C.VipsImage, 0, len(layers)+1)
inputs = append(inputs, image)
for _, l := range layers {
layer, _, err := loadImage(l.buffer)
if err != nil {
return nil, err
}
inputs = append(inputs, layer)
}
return vipsComposite(inputs, mode)
}

func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) {
// Use vips_shrink with the integral reduction
image, err := vipsShrink(image, shrink)
Expand Down
11 changes: 11 additions & 0 deletions vips.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,17 @@ func vipsReduce(input *C.VipsImage, xshrink float64, yshrink float64) (*C.VipsIm
return image, nil
}

func vipsComposite(inputs []*C.VipsImage, mode BlendMode) (*C.VipsImage, error) {
var image *C.VipsImage

err := C.vips_composite_bridge(&inputs[0], &image, C.int(len(inputs)), C.int(mode))
if err != 0 {
return nil, catchVipsError()
}

return image, nil
}

func vipsEmbed(input *C.VipsImage, left, top, width, height int, extend Extend, background Color) (*C.VipsImage, error) {
var image *C.VipsImage

Expand Down
5 changes: 5 additions & 0 deletions vips.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ vips_zoom_bridge(VipsImage *in, VipsImage **out, int xfac, int yfac) {
return vips_zoom(in, out, xfac, yfac, NULL);
}

int
vips_composite_bridge(VipsImage **in, VipsImage **out, int n, int mode) {
return vips_composite(in, out, n, &mode, NULL);
}

int
vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b) {
if (extend == VIPS_EXTEND_BACKGROUND) {
Expand Down

0 comments on commit 165977e

Please sign in to comment.