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

Resizing animated images leads to a toilet roll in some scale values #350

Open
apiks opened this issue Apr 20, 2023 · 1 comment
Open

Resizing animated images leads to a toilet roll in some scale values #350

apiks opened this issue Apr 20, 2023 · 1 comment

Comments

@apiks
Copy link

apiks commented Apr 20, 2023

I'm trying to resize a 500x280 GIF via the Resize function. If the scale parameter is a multiple of 5 (e.g. 0.1, 0.15 or 0.2) it works fine in unrolling the GIF.

The problem is when the scale has isn't a multiple of 5 (e.g. 0.14, 0.22 or 0.66) it can't unroll. It will always return a roll.

This behavior is only for two decimals. With more decimals it becomes even more confusing as 0.265 doesn't work either. Maybe due to the 6 in it?

After fiddling with it for a bit, rounding the scale or forcing a multiple fixes the issue, but also does not allow for the intended scale on the final image.

Additionally I've noticed that in the ResizeWithVScale function it calculates the new page height with int(float64(pageHeight) * scale) which forces it to drop the decimal if it exists. Perhaps this is causing the issue? LibVips takes an int so I understand why it's done.

This only happens with animated images (tested only on GIF). Non-animated images work fine, likely due to their height always being an integer.

To replicate this issue, take a GIF with a width of 500 and try resizing it to the above mentioned scales.

Here's the logic I'm using to resize the GIF:

func resizeImage(img *vips.ImageRef, width, height int, upscale bool) (*vips.ImageRef, error) {
	if width == 0 && height == 0 {
		return img, nil
	}

	if width == 0 {
		scale := float64(height) / float64(img.PageHeight())
		if upscale || scale <= 1 {
			err := img.Resize(scale, vips.KernelAuto)
			if err != nil {
				return nil, err
			}
		}
		return img, nil
	}

	if height == 0 {
		scale := float64(width) / float64(img.Width())
		if upscale || scale <= 1 {
			err := img.Resize(scale, vips.KernelAuto)
			if err != nil {
				return nil, err
			}
		}
		return img, nil
	}

	hScale := float64(width) / float64(img.Width())
	vScale := float64(height) / float64(img.PageHeight())
	if upscale || (hScale <= 1 && vScale <= 1) {
		err := img.ResizeWithVScale(hScale, vScale, vips.KernelAuto)
		if err != nil {
			return nil, err
		}
	}

	return img, nil
}
@TheKigen
Copy link

TheKigen commented Jul 27, 2024

So for anyone wondering what this issue is when resizing animated images (GIF or WebP). The issue is the total unrolled image height must be divisible by the page count to a whole number. It has nothing to do with being divisible by 5.

So if float64(pageHeight)*scale results in a float number instead of a whole number then you will get an unrolled image. I do not know why the govips library includes the + 0.5 in the image Resize function when calculating a new page height.

govips/vips/image.go

Lines 1791 to 1800 in 0c98a28

if pages > 1 {
scale := hScale
if vScale != -1 {
scale = vScale
}
newPageHeight := int(float64(pageHeight)*scale + 0.5)
if err := r.SetPageHeight(newPageHeight); err != nil {
return err
}
}

I made some quick, but dirty code to do resizing of a animated image. It does just width scaling since that is why I needed for my project. It goes and tries to find the nearest width that can properly scale the height to a whole number. Its ugly code that I'm sure someone can write much better but it works for now.

	pages := img.Pages()
	origWidth := img.Width()
	scale := float64(width) / float64(origWidth)
	if pages > 1 {
		pageHeight := img.PageHeight()
		testScale := float64(pageHeight) * scale
		for testScale != float64(int(testScale)) && width != origWidth {
			if width < origWidth {
				width += 1
			} else {
				width -= 1
			}

			scale = float64(width) / float64(origWidth)
			testScale = float64(pageHeight) * scale
		}
	}
	
	if err = img.Resize(scale, vips.KernelAuto); err != nil {
		return err
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants