mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-02-02 11:34:20 +02:00
Improve trim method to accept color to be cut off and hor/ver alignment options (#357)
* Improve trim method to accept color to be cut off and hor/ver alignment options * Make color and equality args truly optional * Add docs for new trim params Co-authored-by: Ilya Melnitskiy <melnitskiy_i_m@onyx-team.com>
This commit is contained in:
parent
6eba90372f
commit
4bd745f764
@ -173,16 +173,21 @@ Defines an area of the image to be processed (crop before resize).
|
||||
#### Trim
|
||||
|
||||
```
|
||||
trim:%threshold
|
||||
t:%threshold
|
||||
trim:%threshold:%hex_color:%equal_hor:%equal_ver
|
||||
t:%threshold:%hex_color:%equal_hor:%equal_ver
|
||||
```
|
||||
|
||||
Removes surrounding background.
|
||||
|
||||
* `threshold` - color similarity tolerance.
|
||||
* `threshold` - color similarity tolerance (the only required argument).
|
||||
* `hex_color` - hex-coded value of the color that needs to be cut off.
|
||||
* `equal_hor` - cut only equal parts from left and right sides. That means that if 10px of background can be cut off from left and 5px from right then 5px will be cut off from both sides. For example, it can be useful if objects on your images are centered but have non-symmetrical shadow.
|
||||
* `equal_ver` - acts like `equal_hor` but for top/bottom sides.
|
||||
|
||||
**⚠️Warning:** Trimming requires an image to be fully loaded into memory. This disables scale-on-load and significantly increases memory usage and processing time. Use it carefully with large images.
|
||||
|
||||
**📝Note:** If you know background color of your images then setting it explicitly via `hex_color` will also save some resources because libvips won't detect it automatically.
|
||||
|
||||
**📝Note:** Trimming of animated images is not supported.
|
||||
|
||||
#### Quality
|
||||
|
@ -302,7 +302,7 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
|
||||
)
|
||||
|
||||
if po.Trim.Enabled {
|
||||
if err = img.Trim(po.Trim.Threshold); err != nil {
|
||||
if err = img.Trim(po.Trim.Threshold, po.Trim.Smart, po.Trim.Color, po.Trim.EqualHor, po.Trim.EqualVer); err != nil {
|
||||
return err
|
||||
}
|
||||
trimmed = true
|
||||
|
@ -103,6 +103,10 @@ type cropOptions struct {
|
||||
type trimOptions struct {
|
||||
Enabled bool
|
||||
Threshold float64
|
||||
Smart bool
|
||||
Color rgbColor
|
||||
EqualHor bool
|
||||
EqualVer bool
|
||||
}
|
||||
|
||||
type watermarkOptions struct {
|
||||
@ -204,7 +208,7 @@ func newProcessingOptions() *processingOptions {
|
||||
Gravity: gravityOptions{Type: gravityCenter},
|
||||
Enlarge: false,
|
||||
Extend: extendOptions{Enabled: false, Gravity: gravityOptions{Type: gravityCenter}},
|
||||
Trim: trimOptions{Enabled: false, Threshold: 10},
|
||||
Trim: trimOptions{Enabled: false, Threshold: 10, Smart: true},
|
||||
Quality: conf.Quality,
|
||||
MaxBytes: 0,
|
||||
Format: imageTypeUnknown,
|
||||
@ -553,15 +557,34 @@ func applyCropOption(po *processingOptions, args []string) error {
|
||||
}
|
||||
|
||||
func applyTrimOption(po *processingOptions, args []string) error {
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("Invalid crop arguments: %v", args)
|
||||
nArgs := len(args)
|
||||
|
||||
if nArgs > 4 {
|
||||
return fmt.Errorf("Invalid trim arguments: %v", args)
|
||||
}
|
||||
|
||||
if t, err := strconv.ParseFloat(args[0], 64); err == nil && t >= 0 {
|
||||
po.Trim.Enabled = true
|
||||
po.Trim.Threshold = t
|
||||
} else {
|
||||
return fmt.Errorf("Invalid trim treshold: %s", args[0])
|
||||
return fmt.Errorf("Invalid trim threshold: %s", args[0])
|
||||
}
|
||||
|
||||
if nArgs > 1 && len(args[1]) > 0 {
|
||||
if c, err := colorFromHex(args[1]); err == nil {
|
||||
po.Trim.Color = c
|
||||
po.Trim.Smart = false
|
||||
} else {
|
||||
return fmt.Errorf("Invalid trim color: %s", args[1])
|
||||
}
|
||||
}
|
||||
|
||||
if nArgs > 2 && len(args[2]) > 0 {
|
||||
po.Trim.EqualHor = parseBoolOption(args[2])
|
||||
}
|
||||
|
||||
if nArgs > 3 && len(args[3]) > 0 {
|
||||
po.Trim.EqualVer = parseBoolOption(args[3])
|
||||
}
|
||||
|
||||
return nil
|
||||
|
43
vips.c
43
vips.c
@ -396,7 +396,9 @@ vips_extract_area_go(VipsImage *in, VipsImage **out, int left, int top, int widt
|
||||
}
|
||||
|
||||
int
|
||||
vips_trim(VipsImage *in, VipsImage **out, double threshold) {
|
||||
vips_trim(VipsImage *in, VipsImage **out, double threshold,
|
||||
gboolean smart, double r, double g, double b,
|
||||
gboolean equal_hor, gboolean equal_ver) {
|
||||
#if VIPS_SUPPORT_FIND_TRIM
|
||||
VipsImage *tmp;
|
||||
|
||||
@ -410,15 +412,20 @@ vips_trim(VipsImage *in, VipsImage **out, double threshold) {
|
||||
|
||||
double *bg;
|
||||
int bgn;
|
||||
VipsArrayDouble *bga;
|
||||
|
||||
if (vips_getpoint(tmp, &bg, &bgn, 0, 0, NULL)) {
|
||||
clear_image(&tmp);
|
||||
return 1;
|
||||
if (smart) {
|
||||
if (vips_getpoint(tmp, &bg, &bgn, 0, 0, NULL)) {
|
||||
clear_image(&tmp);
|
||||
return 1;
|
||||
}
|
||||
bga = vips_array_double_new(bg, bgn);
|
||||
} else {
|
||||
bga = vips_array_double_newv(3, r, g, b);
|
||||
bg = 0;
|
||||
}
|
||||
|
||||
VipsArrayDouble *bga = vips_array_double_new(bg, bgn);
|
||||
|
||||
int left, top, width, height;
|
||||
int left, right, top, bot, width, height, diff;
|
||||
|
||||
if (vips_find_trim(tmp, &left, &top, &width, &height, "background", bga, "threshold", threshold, NULL)) {
|
||||
clear_image(&tmp);
|
||||
@ -427,6 +434,28 @@ vips_trim(VipsImage *in, VipsImage **out, double threshold) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (equal_hor) {
|
||||
right = in->Xsize - left - width;
|
||||
diff = right - left;
|
||||
if (diff > 0) {
|
||||
width += diff;
|
||||
} else if (diff < 0) {
|
||||
left = right;
|
||||
width -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (equal_ver) {
|
||||
bot = in->Ysize - top - height;
|
||||
diff = bot - top;
|
||||
if (diff > 0) {
|
||||
height += diff;
|
||||
} else if (diff < 0) {
|
||||
top = bot;
|
||||
height -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
clear_image(&tmp);
|
||||
vips_area_unref((VipsArea *)bga);
|
||||
g_free(bg);
|
||||
|
6
vips.go
6
vips.go
@ -367,14 +367,16 @@ func (img *vipsImage) SmartCrop(width, height int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (img *vipsImage) Trim(threshold float64) error {
|
||||
func (img *vipsImage) Trim(threshold float64, smart bool, color rgbColor, equalHor bool, equalVer bool) error {
|
||||
var tmp *C.VipsImage
|
||||
|
||||
if err := img.CopyMemory(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if C.vips_trim(img.VipsImage, &tmp, C.double(threshold)) != 0 {
|
||||
if C.vips_trim(img.VipsImage, &tmp, C.double(threshold),
|
||||
gbool(smart), C.double(color.R), C.double(color.G), C.double(color.B),
|
||||
gbool(equalHor), gbool(equalVer)) != 0 {
|
||||
return vipsError()
|
||||
}
|
||||
|
||||
|
4
vips.h
4
vips.h
@ -66,7 +66,9 @@ int vips_flip_horizontal_go(VipsImage *in, VipsImage **out);
|
||||
|
||||
int vips_extract_area_go(VipsImage *in, VipsImage **out, int left, int top, int width, int height);
|
||||
int vips_smartcrop_go(VipsImage *in, VipsImage **out, int width, int height);
|
||||
int vips_trim(VipsImage *in, VipsImage **out, double threshold);
|
||||
int vips_trim(VipsImage *in, VipsImage **out, double threshold,
|
||||
gboolean smart, double r, double g, double b,
|
||||
gboolean equal_hor, gboolean equal_ver);
|
||||
|
||||
int vips_gaussblur_go(VipsImage *in, VipsImage **out, double sigma);
|
||||
int vips_sharpen_go(VipsImage *in, VipsImage **out, double sigma);
|
||||
|
Loading…
x
Reference in New Issue
Block a user