sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Ensure combination of resize and rotate operations is ordered (to match extract and rotate)

Open tomasklaen opened this issue 1 year ago • 4 comments

Possible bug

  • [x] Running npm install sharp completes without error.
  • [x] Running node -e "require('sharp')" completes without error.
  • [x] I am using the latest version of sharp as reported by npm view sharp dist-tags.latest.
  System:
    OS: Windows 10 10.0.16299
    CPU: (4) x64 Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
    Memory: 10.08 GB / 15.95 GB
  Binaries:
    Node: 18.7.0 - C:\Program Files\nodejs\node.EXE
    npm: 8.15.0 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    sharp: ^0.30.7 => 0.30.7

What are the steps to reproduce?

Resize an image into specific dimensions with fill fit mode, then try to rotate it 90 degrees.

What is the expected behavior?

Result image should be rotated, and have {oldHeight}x{oldWidth} dimensions, instead, it IS rotated, but somehow retained {oldWidth}x{oldHeight} dimensions.

Code sample

import sharp from 'sharp';

const image = sharp('./in.jpg');
image.resize({width: 400, height: 500, fit: 'fill'});
image.rotate(90);
await image.toFile('./out.jpg');

Sample image

painting.jpg

tomasklaen avatar Aug 03 '22 09:08 tomasklaen

Happens for contain and cover fit modes as well.

I currently have to do this intermediate step in between resize and rotate to "fix" it by I guess resetting whatever is being mangled internally:

image = sharp(await image.png().toBuffer());

tomasklaen avatar Aug 04 '22 07:08 tomasklaen

As you've seen, you'll currently need to break this operation into two pipelines - see #1922, #2086 and related issues.

This has come up a few times, and I've been meaning to modify sharp to ensure that the combination of resize+rotate is also "ordered" to match the behaviour of extract+rotate.

https://sharp.pixelplumbing.com/api-operation#rotate

Method order is important when both rotating and extracting regions, for example rotate(x).extract(y) will produce a different result to extract(y).rotate(x).

Let's use this issue to track that enhancement.

lovell avatar Aug 04 '22 18:08 lovell

I'm running into more and more chaining issues where sharp produces unexpected results. For example:

image.extract(...).rotate(90).extract(...);

The result is a completely different region than expected. I guess the second extract overwrites the first one?

So what I currently have to do is, similar to the above workaround, I made this flush function:

const flush = async () => {
	const {data, info} = await image.raw().toBuffer({resolveWithObject: true});
	image = sharp(data, {raw: info});
};

And I call it in between every operation just to be sure sharp chaining issues don't break something.

If sharp doesn't really support chaining operations, it shouldn't encourage it via it's API design 😞. Or have some big warnings everywhere that only one operation type per chain is allowed, and only certain orders are supported, and all other quirks and limitations I'm currently not aware of.

tomasklaen avatar Aug 06 '22 08:08 tomasklaen

Please see the discussion at #241

lovell avatar Aug 09 '22 16:08 lovell

Commit https://github.com/lovell/sharp/commit/212a6e7519413e69d6eb2e875ca750b5f76771b0 improves the situation slightly, adding examples and test cases, as well as emitting warnings when previous calls in the same pipeline will be ignored. This will be in v0.31.0.

lovell avatar Aug 21 '22 19:08 lovell

v0.31.0 is now available with these improvements. Let's continue the other parts of this discussion at #241

lovell avatar Sep 05 '22 09:09 lovell