-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Rotation sugar and more #4151
base: main
Are you sure you want to change the base?
Rotation sugar and more #4151
Changes from all commits
b9f3c23
0f7d21a
ef5f283
1837cef
44d7cd0
b52c48c
5bd0471
800b22d
c3e9be3
30296cf
c5dd3e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -364,24 +364,72 @@ declare namespace sharp { | |
//#region Operation functions | ||
|
||
/** | ||
* Rotate the output image by either an explicit angle or auto-orient based on the EXIF Orientation tag. | ||
* Rotate the output image by either an explicit angle | ||
* or auto-orient based on the EXIF `Orientation` tag. | ||
* | ||
* If an angle is provided, it is converted to a valid positive degree rotation. For example, -450 will produce a 270deg rotation. | ||
* If an angle is provided, it is converted to a valid positive degree rotation. | ||
* For example, `-450` will produce a 270 degree rotation. | ||
* | ||
* When rotating by an angle other than a multiple of 90, the background colour can be provided with the background option. | ||
* When rotating by an angle other than a multiple of 90, | ||
* the background colour can be provided with the `background` option. | ||
* | ||
* If no angle is provided, it is determined from the EXIF data. Mirroring is supported and may infer the use of a flip operation. | ||
* If no angle is provided, it is determined from the EXIF data. | ||
* Mirroring is supported and may infer the use of a flip operation. | ||
* | ||
* The use of rotate implies the removal of the EXIF Orientation tag, if any. | ||
* The use of `rotate` without an angle will remove the EXIF `Orientation` tag, if any. | ||
Comment on lines
+367
to
+379
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not only a change to reflect the doc improvements, but also to sync with the docs created in the |
||
* | ||
* 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). | ||
* @param angle angle of rotation. (optional, default auto) | ||
* @param options if present, is an Object with optional attributes. | ||
* Only one rotation can occur per pipeline (aside from an initial call without | ||
* arguments to orient via EXIF data). Previous calls to `rotate` in the same | ||
* pipeline will be ignored. | ||
* | ||
* Multi-page images can only be rotated by 180 degrees. | ||
* | ||
* Method order is important when rotating, resizing and/or extracting regions, | ||
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`. | ||
* | ||
* @example | ||
* const pipeline = sharp() | ||
* .rotate() | ||
* .resize(null, 200) | ||
* .toBuffer(function (err, outputBuffer, info) { | ||
* // outputBuffer contains 200px high JPEG image data, | ||
* // auto-rotated using EXIF Orientation tag | ||
* // info.width and info.height contain the dimensions of the resized image | ||
* }); | ||
* readableStream.pipe(pipeline); | ||
* | ||
* @example | ||
* const rotateThenResize = await sharp(input) | ||
* .rotate(90) | ||
* .resize({ width: 16, height: 8, fit: 'fill' }) | ||
* .toBuffer(); | ||
* const resizeThenRotate = await sharp(input) | ||
* .resize({ width: 16, height: 8, fit: 'fill' }) | ||
* .rotate(90) | ||
* .toBuffer(); | ||
* | ||
* @param {number} [angle=auto] angle of rotation. | ||
* @param {Object} [options] - if present, is an Object with optional attributes. | ||
* @param {string|Object} [options.background="#000000"] parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. | ||
* @returns {Sharp} | ||
* @throws {Error} Invalid parameters | ||
* @returns A sharp instance that can be used to chain operations | ||
*/ | ||
rotate(angle?: number, options?: RotateOptions): Sharp; | ||
|
||
/** | ||
* Alias for calling `rotate()` with no arguments, which orients the image based | ||
* on EXIF orientsion. | ||
* | ||
* This operation is aliased to emphasize its purpose, helping to remove any | ||
* confusion between rotation and orientation. | ||
* | ||
* @example | ||
* const output = await sharp(input).autoOrient().toBuffer(); | ||
* | ||
* @returns {Sharp} | ||
*/ | ||
autoOrient(): Sharp | ||
|
||
/** | ||
* Flip the image about the vertical Y axis. This always occurs after rotation, if any. | ||
* The use of flip implies the removal of the EXIF Orientation tag, if any. | ||
|
@@ -900,6 +948,13 @@ declare namespace sharp { | |
} | ||
|
||
interface SharpOptions { | ||
/** | ||
* Auto-orient based on the EXIF `Orientation` tag, if present. | ||
* Mirroring is supported and may infer the use of a flip operation. | ||
* | ||
* Using this option will remove the EXIF `Orientation` tag, if any. | ||
*/ | ||
autoOrient?: boolean; | ||
/** | ||
* When to abort processing of invalid pixel data, one of (in order of sensitivity): | ||
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning') | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -7,40 +7,26 @@ const color = require('color'); | |||||||||
const is = require('./is'); | ||||||||||
|
||||||||||
/** | ||||||||||
* Rotate the output image by either an explicit angle | ||||||||||
* or auto-orient based on the EXIF `Orientation` tag. | ||||||||||
* Rotate the output image. | ||||||||||
* | ||||||||||
* If an angle is provided, it is converted to a valid positive degree rotation. | ||||||||||
* The provided angle is converted to a valid positive degree rotation. | ||||||||||
* For example, `-450` will produce a 270 degree rotation. | ||||||||||
* | ||||||||||
* When rotating by an angle other than a multiple of 90, | ||||||||||
* the background colour can be provided with the `background` option. | ||||||||||
* | ||||||||||
* If no angle is provided, it is determined from the EXIF data. | ||||||||||
* Mirroring is supported and may infer the use of a flip operation. | ||||||||||
* | ||||||||||
* The use of `rotate` without an angle will remove the EXIF `Orientation` tag, if any. | ||||||||||
* For backwards compatibility, if no angle is provided, `.autoOrient()` will be called. | ||||||||||
* | ||||||||||
* Only one rotation can occur per pipeline. | ||||||||||
* Previous calls to `rotate` in the same pipeline will be ignored. | ||||||||||
* Only one rotation can occur per pipeline (aside from an initial call without | ||||||||||
* arguments to orient via EXIF data). Previous calls to `rotate` in the same | ||||||||||
* pipeline will be ignored. | ||||||||||
* | ||||||||||
* Multi-page images can only be rotated by 180 degrees. | ||||||||||
* | ||||||||||
* Method order is important when rotating, resizing and/or extracting regions, | ||||||||||
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`. | ||||||||||
* | ||||||||||
* @example | ||||||||||
* const pipeline = sharp() | ||||||||||
* .rotate() | ||||||||||
* .resize(null, 200) | ||||||||||
* .toBuffer(function (err, outputBuffer, info) { | ||||||||||
* // outputBuffer contains 200px high JPEG image data, | ||||||||||
* // auto-rotated using EXIF Orientation tag | ||||||||||
* // info.width and info.height contain the dimensions of the resized image | ||||||||||
* }); | ||||||||||
* readableStream.pipe(pipeline); | ||||||||||
* | ||||||||||
* @example | ||||||||||
* const rotateThenResize = await sharp(input) | ||||||||||
* .rotate(90) | ||||||||||
* .resize({ width: 16, height: 8, fit: 'fill' }) | ||||||||||
|
@@ -57,12 +43,12 @@ const is = require('./is'); | |||||||||
* @throws {Error} Invalid parameters | ||||||||||
*/ | ||||||||||
function rotate (angle, options) { | ||||||||||
if (this.options.useExifOrientation || this.options.angle || this.options.rotationAngle) { | ||||||||||
if (!is.defined(angle)) { return this.autoOrient(); } | ||||||||||
|
||||||||||
if (this.options.angle || this.options.rotationAngle) { | ||||||||||
this.options.debuglog('ignoring previous rotate options'); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This warning says that previous rotations will be ignored, but that isn't actually true unless you reset
Want me to throw in a fix for that? Or alter the warning? Or just leave as is?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please to fixing this at the same time. |
||||||||||
} | ||||||||||
if (!is.defined(angle)) { | ||||||||||
this.options.useExifOrientation = true; | ||||||||||
} else if (is.integer(angle) && !(angle % 90)) { | ||||||||||
if (is.integer(angle) && !(angle % 90)) { | ||||||||||
this.options.angle = angle; | ||||||||||
} else if (is.number(angle)) { | ||||||||||
this.options.rotationAngle = angle; | ||||||||||
|
@@ -81,6 +67,34 @@ function rotate (angle, options) { | |||||||||
return this; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Auto-orient based on the EXIF `Orientation` tag, then remove the tag. | ||||||||||
* Mirroring is supported and may infer the use of a flip operation. | ||||||||||
* | ||||||||||
* Previous or subsequent use of `rotate(angle)` and either `flip()` or `flop()` | ||||||||||
* will logically occur after auto-orientation, regardless of call order. | ||||||||||
* | ||||||||||
* @example | ||||||||||
* const output = await sharp(input).autoOrient().toBuffer(); | ||||||||||
* | ||||||||||
* @example | ||||||||||
* const pipeline = sharp() | ||||||||||
* .autoOrient() | ||||||||||
* .resize(null, 200) | ||||||||||
* .toBuffer(function (err, outputBuffer, info) { | ||||||||||
* // outputBuffer contains 200px high JPEG image data, | ||||||||||
* // auto-oriented using EXIF Orientation tag | ||||||||||
* // info.width and info.height contain the dimensions of the resized image | ||||||||||
* }); | ||||||||||
* readableStream.pipe(pipeline); | ||||||||||
* | ||||||||||
* @returns {Sharp} | ||||||||||
*/ | ||||||||||
function autoOrient () { | ||||||||||
this.options.input.autoOrient = true; | ||||||||||
return this; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Mirror the image vertically (up-down) about the x-axis. | ||||||||||
* This always occurs before rotation, if any. | ||||||||||
|
@@ -895,6 +909,7 @@ function modulate (options) { | |||||||||
*/ | ||||||||||
module.exports = function (Sharp) { | ||||||||||
Object.assign(Sharp.prototype, { | ||||||||||
autoOrient, | ||||||||||
rotate, | ||||||||||
flip, | ||||||||||
flop, | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It occurs to me now that I am trying to test this new version of Sharp with
@sveltejs/enhanced-img
that the metadata is not adjusted in time for some useful operations to take place. For example:The above will show the metadata before any auto-orientation takes place, which seems like a headache from the perspective of someone using the tool. If I am asking sharp to auto-orient the image, why would I want the old width and height?
I see several ways to work this issue:
getNormalSize
function in the docs is all anyone needs.autoOrient
is true, drop "orientation" and swap width and height as necessary from metadata..metadata({ autoOrient: boolean })
that opts you in to view the adjusted metadata.autoOrientMetadata(metadata)
that devs can use as needed on metadata objects.appliedOrientation
property that reports thewidth
andheight
again, changing them if necessary based on exif orientation:It seems like a useful thing to just include that metadata so developers can see it when they inspect the thing and remain blissfully ignorant about which transforms are needed for each orientation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of adding extra fields to the metadata that include the dimensions after applying orientation, which is closest to option 5 here. There have been a few people ask questions along the lines of "why are the metadata dimensions not what I expect?" and this would help answer it.