Scale both sides of a fit=cover image by the same amount (part of #282)
check / check (push) Waiting to run
check / check (push) Waiting to run
cropCentreAndResize cut the centre to whole source pixels and forced that to the requested size, so the two sides were scaled by different amounts: a 16x16 source at 100x60 came out squashed by 4%. It now widens the centre to whole source pixels and lets thumbnail with a centre crop scale that by the one amount next uses and cut the output from it. thumbnail copies the scaled image into memory, which is larger than the output by less than one scaled source pixel on each side. TODO.md says so. Model: opus-5-5
This commit is contained in:
@@ -530,35 +530,36 @@ func (p *ImageProcessor) resize(
|
||||
}
|
||||
}
|
||||
|
||||
// cropCentreAndResize cuts img to its centre, the largest area it holds with
|
||||
// the proportions of width x height, and resizes that to width x height.
|
||||
// This is the crop libvips' thumbnail makes with InterestingCentre, but
|
||||
// libvips scales the whole image to cover the size and copies the result
|
||||
// into memory before it crops, and for a thin image that is far larger than
|
||||
// the image or the output: 8192x64 at 8192x8192 is 1048576x8192, about 24
|
||||
// GiB. Cropping first makes no image larger than img or the output.
|
||||
// cropCentreAndResize scales img by the same amount on both sides, the
|
||||
// least that makes it cover width x height, and cuts the centre width x
|
||||
// height out of it, as libvips' thumbnail does with InterestingCentre.
|
||||
// thumbnail alone scales the whole image and copies the result into memory
|
||||
// before it cuts, and for a thin image that is far larger than the image or
|
||||
// the output: 8192x64 at 8192x8192 is 1048576x8192, about 24 GiB. So the
|
||||
// centre is cut from img first, widened to whole pixels of img, which adds
|
||||
// less than one pixel of img on each side, and thumbnail scales and cuts
|
||||
// only that.
|
||||
func cropCentreAndResize(img *vips.ImageRef, width, height int) error {
|
||||
imgW, imgH := img.Width(), img.Height()
|
||||
|
||||
// An image wider than the requested proportions keeps its height and
|
||||
// loses width, any other keeps its width and loses height; the side
|
||||
// shortened is rounded to the nearest pixel and is at least 1
|
||||
cropW, cropH := imgW, imgH
|
||||
// loses width down to imgH*width/height, any other keeps its width and
|
||||
// loses height down to imgW*height/width. Half of what is lost comes
|
||||
// off each side, rounded down to whole pixels, so the part kept holds
|
||||
// all of the centre.
|
||||
left, top := 0, 0
|
||||
if imgW*height > imgH*width {
|
||||
cropW = max(1, (imgH*width+height/2)/height)
|
||||
left = (imgW*height - imgH*width) / (2 * height) //nolint:mnd // each side
|
||||
} else {
|
||||
cropH = max(1, (imgW*height+width/2)/width)
|
||||
top = (imgH*width - imgW*height) / (2 * width) //nolint:mnd // each side
|
||||
}
|
||||
|
||||
// Half of what is cut comes off each side
|
||||
left, top := (imgW-cropW)/2, (imgH-cropH)/2 //nolint:mnd // half the cut
|
||||
|
||||
err := img.ExtractArea(left, top, cropW, cropH)
|
||||
err := img.ExtractArea(left, top, imgW-2*left, imgH-2*top)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return img.ThumbnailWithSize(width, height, vips.InterestingNone, vips.SizeForce)
|
||||
return img.Thumbnail(width, height, vips.InterestingCentre)
|
||||
}
|
||||
|
||||
const defaultQuality = 85
|
||||
|
||||
Reference in New Issue
Block a user