Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animated GIF support for ExtractArea #259

Merged
merged 10 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions vips/conversion.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,35 @@ int extract_image_area(VipsImage *in, VipsImage **out, int left, int top,
return vips_extract_area(in, out, left, top, width, height, NULL);
}

int extract_area_multi_page(VipsImage *in, VipsImage **out, int left, int top, int width, int height) {
VipsObject *base = VIPS_OBJECT(vips_image_new());
int page_height = vips_image_get_page_height(in);
int n_pages = in->Ysize / page_height;

VipsImage **page = (VipsImage **) vips_object_local_array(base, n_pages);
VipsImage **copy = (VipsImage **) vips_object_local_array(base, 1);

// split image into cropped frames
for (int i = 0; i < n_pages; i++) {
if(vips_extract_area(in, &page[i], left, page_height * i + top, width, height, NULL)) {
g_object_unref(base);
return -1;
}
}
// reassemble frames and set page height
// copy before modifying metadata
if(
vips_arrayjoin(page, &copy[0], n_pages, "across", 1, NULL) ||
vips_copy(copy[0], out, NULL)
) {
g_object_unref(base);
return -1;
}
vips_image_set_int(*out, VIPS_META_PAGE_HEIGHT, height);
g_object_unref(base);
return 0;
}

int extract_band(VipsImage *in, VipsImage **out, int band, int num) {
if (num > 0) {
return vips_extract_band(in, out, band, "n", num, NULL);
Expand Down
11 changes: 11 additions & 0 deletions vips/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ func vipsExtractArea(in *C.VipsImage, left, top, width, height int) (*C.VipsImag
return out, nil
}

func vipsExtractAreaMultiPage(in *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) {
incOpCounter("extractAreaMultiPage")
var out *C.VipsImage

if err := C.extract_area_multi_page(in, &out, C.int(left), C.int(top), C.int(width), C.int(height)); err != 0 {
return nil, handleImageError(out)
}

return out, nil
}

// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-extract-band
func vipsExtractBand(in *C.VipsImage, band, num int) (*C.VipsImage, error) {
incOpCounter("extractBand")
Expand Down
2 changes: 2 additions & 0 deletions vips/conversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ int flip_image(VipsImage *in, VipsImage **out, int direction);

int extract_image_area(VipsImage *in, VipsImage **out, int left, int top,
int width, int height);
int extract_area_multi_page(VipsImage *in, VipsImage **out, int left, int top,
int width, int height);

int extract_band(VipsImage *in, VipsImage **out, int band, int num);

Expand Down
3 changes: 1 addition & 2 deletions vips/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import (

var (
// ErrUnsupportedImageFormat when image type is unsupported
ErrUnsupportedImageFormat = errors.New("unsupported image format")
ErrUnsupportedMultiPageOperation = errors.New("unsupported operation for multi-page image")
ErrUnsupportedImageFormat = errors.New("unsupported image format")
)

func handleImageError(out *C.VipsImage) error {
Expand Down
29 changes: 13 additions & 16 deletions vips/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -1162,15 +1162,20 @@ func (r *ImageRef) AutoRotate() error {

// ExtractArea crops the image to a specified area
func (r *ImageRef) ExtractArea(left, top, width, height int) error {
if err := r.multiPageNotSupported(); err != nil {
return err
}

out, err := vipsExtractArea(r.image, left, top, width, height)
if err != nil {
return err
if r.Height() > r.PageHeight() {
// use animated extract area if more than 1 pages loaded
out, err := vipsExtractAreaMultiPage(r.image, left, top, width, height)
if err != nil {
return err
}
r.setImage(out)
} else {
out, err := vipsExtractArea(r.image, left, top, width, height)
if err != nil {
return err
}
r.setImage(out)
}
r.setImage(out)
return nil
}

Expand Down Expand Up @@ -1711,14 +1716,6 @@ func (r *ImageRef) newMetadata(format ImageType) *ImageMetadata {
}
}

func (r *ImageRef) multiPageNotSupported() error {
if r.Pages() > 1 {
return ErrUnsupportedMultiPageOperation
}

return nil
}

// Pixelate applies a simple pixelate filter to the image
func Pixelate(imageRef *ImageRef, factor float64) (err error) {
if factor < 1 {
Expand Down
10 changes: 10 additions & 0 deletions vips/image_gif_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,13 @@ func TestImage_GIF_Animated_Rotate270(t *testing.T) {
nil,
nil)
}

func TestImage_GIF_Animated_ExtractArea(t *testing.T) {
goldenAnimatedTest(t, resources+"gif-animated.gif",
-1,
func(img *ImageRef) error {
return img.ExtractArea(10, 20, 80, 90)
},
nil,
nil)
}
9 changes: 0 additions & 9 deletions vips/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -967,15 +967,6 @@ func TestImageRef_JP2K(t *testing.T) {
assert.Equal(t, ImageTypeJP2K, metadata.Format)
}

func TestImageRef_ExtractArea_MultiPage_Unsupported(t *testing.T) {
Startup(nil)
image, err := NewImageFromFile(resources + "gif-animated.gif")
require.NoError(t, err)

err = image.ExtractArea(1, 2, 3, 4)
assert.Error(t, err)
}

// TODO unit tests to cover:
// NewImageFromReader failing test
// NewImageFromFile failing test
Expand Down